home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / xwindows / demos / xfract_1.z / xfract_1 / xfractint-1.06 / hc.c < prev    next >
C/C++ Source or Header  |  1992-09-28  |  83KB  |  3,924 lines

  1.  
  2. /*
  3.  * hc.c
  4.  *
  5.  * Stand-alone FRACTINT help compiler.  Compile in the COMPACT memory model.
  6.  *
  7.  * See HC.DOC for source file syntax.
  8.  *
  9.  *
  10.  * Revision History:
  11.  *
  12.  *   02-26-91 EAN     Initial version.
  13.  *
  14.  *   03-21-91 EAN     Modified for automatic paragraph formatting.
  15.  *              Added several new commands:
  16.  *             Format[+/-]  Enable/disable paragraph formatting
  17.  *             Doc[+/-]     Enable/disable output to document.
  18.  *             Online[+/-]  Enable/disable output to online help.
  19.  *             Label=       Defines a label. Replaces ~(...)
  20.  *             FF          Forces a form-feed.  Replaces ~~
  21.  *             FormatExclude=val Exclude lines past val from
  22.  *                      formatting.  If before any topic sets
  23.  *                      global default, otherwise set local.
  24.  *             FormatExclude= Set to global default.
  25.  *             FormatExclude=n Disable exclusion. (global or local)
  26.  *             FormatExclude[+/-] Enable/disable format exclusion.
  27.  *             Center[+/-]  Enable/disable centering of text.
  28.  *             \ before nl  Forces the end of a paragraph
  29.  *              Support for commands embedded in text with new
  30.  *              ~(...) format.
  31.  *              Support for multiple commands on a line separated by
  32.  *              commas.
  33.  *              Support for implict links; explicit links must now
  34.  *              start with an equal sign.
  35.  *   04-03-91 EAN     Added "include" command (works like #include)
  36.  *   04-10-91 EAN     Added support for "data" topics.
  37.  *              Added Comment/EndComment commands for multi-line
  38.  *             comments.
  39.  *              Added CompressSpaces[+/-] command.
  40.  *              Added DocContents command for document printing.
  41.  *              Added BinInc command which includes a binary file
  42.  *             in a data topic.
  43.  *              Fixed tables to flow down instead of across the page.
  44.  *             Makes no allowances for page breaks within tables.
  45.  *
  46.  */
  47.  
  48.  
  49. #define HC_C
  50.  
  51. #define INCLUDE_COMMON  /* tell helpcom.h to include common code */
  52.  
  53.  
  54. #include <stdio.h>
  55. #ifndef XFRACT
  56. #include <io.h>
  57. #include <stdarg.h>
  58. #else
  59. #include <varargs.h>
  60. #define strupr strlwr
  61. #endif
  62. #include <fcntl.h>
  63. #include <stdlib.h>
  64. #include <string.h>
  65. #include <ctype.h>
  66.  
  67. #ifdef __TURBOC__
  68. #   include <dir.h>
  69. #   define FNSPLIT fnsplit
  70. #else
  71. #   define MAXFILE _MAX_FNAME
  72. #   define MAXEXT  _MAX_EXT
  73. #   define FNSPLIT _splitpath
  74. #endif
  75.  
  76.  
  77. #include <assert.h>
  78. #include "helpcom.h"
  79.  
  80.  
  81. /*
  82.  * When defined, SHOW_ERROR_LINE will cause the line number in HC.C where
  83.  * errors/warnings/messages are generated to be displayed at the start of
  84.  * the line.
  85.  *
  86.  * Used when debugging HC.  Also useful for finding the line (in HC.C) that
  87.  * generated a error or warning.
  88.  */
  89.  
  90. #ifndef XFRACT
  91. #define SHOW_ERROR_LINE
  92. #endif
  93.  
  94.  
  95. #define DEFAULT_SRC_FNAME "help.src"
  96. #define DEFAULT_HLP_FNAME "fractint.hlp"
  97. #define DEFAULT_EXE_FNAME "fractint.exe"
  98. #define DEFAULT_DOC_FNAME "fractint.doc"
  99.  
  100. #define TEMP_FNAME      "HC.$$$"
  101. #define SWAP_FNAME      "HCSWAP.$$$"
  102.  
  103. #define MAX_ERRORS      (25)     /* stop after this many errors */
  104. #define MAX_WARNINGS      (25)     /* stop after this many warnings */
  105.                  /* 0 = never stop */
  106.  
  107. #define INDEX_LABEL      "HELP_INDEX"
  108. #define DOCCONTENTS_TITLE "DocContent"
  109.  
  110.  
  111.  
  112. #define BUFFER_SIZE   (24*1024)
  113.  
  114.  
  115. typedef struct
  116.    {
  117.    int        type;         /* 0 = name is topic title, 1 = name is label, */
  118.                  /*   2 = "special topic"; name is NULL and */
  119.                  /*   topic_num/topic_off is valid */
  120.    int        topic_num;         /* topic number to link to */
  121.    unsigned topic_off;         /* offset into topic to link to */
  122.    int        doc_page;         /* document page # to link to */
  123.    char    *name;         /* name of label or title of topic to link to */
  124.    char    *srcfile;         /* .SRC file link appears in */
  125.    int        srcline;         /* .SRC file line # link appears in */
  126.    } LINK;
  127.  
  128.  
  129. typedef struct
  130.    {
  131.    unsigned offset;     /* offset from start of topic text */
  132.    unsigned length;     /* length of page (in chars) */
  133.    int        margin;     /* if > 0 then page starts in_para and text */
  134.                 /* should be indented by this much */
  135.    } PAGE;
  136.  
  137.  
  138. /* values for TOPIC.flags */
  139.  
  140. #define TF_IN_DOC  (1)         /* 1 if topic is part of the printed document */
  141. #define TF_DATA    (2)         /* 1 if it is a "data" topic */
  142.  
  143.  
  144. typedef struct
  145.    {
  146.    unsigned  flags;         /* see #defines for TF_??? */
  147.    int         doc_page;         /* page number in document where topic starts */
  148.    unsigned  title_len;      /* length of title */
  149.    char     *title;         /* title for this topic */
  150.    int         num_page;         /* number of pages */
  151.    PAGE     *page;         /* list of pages */
  152.    unsigned  text_len;         /* lenth of topic text */
  153.    long      text;         /* topic text (all pages) */
  154.    long      offset;         /* offset to topic from start of file */
  155.    } TOPIC;
  156.  
  157.  
  158. typedef struct
  159.    {
  160.    char    *name;         /* its name */
  161.    int        topic_num;         /* topic number */
  162.    unsigned topic_off;         /* offset of label in the topic's text */
  163.    int        doc_page;
  164.    } LABEL;
  165.  
  166.  
  167. /* values for CONTENT.flags */
  168.  
  169. #define CF_NEW_PAGE  (1)     /* true if section starts on a new page */
  170.  
  171.  
  172. #define MAX_CONTENT_TOPIC (10)
  173.  
  174.  
  175. typedef struct
  176.    {
  177.    unsigned  flags;
  178.    char     *id;
  179.    char     *name;
  180.    int         doc_page;
  181.    unsigned  page_num_pos;
  182.    int         num_topic;
  183.    char      is_label[MAX_CONTENT_TOPIC];
  184.    char     *topic_name[MAX_CONTENT_TOPIC];
  185.    int         topic_num[MAX_CONTENT_TOPIC];
  186.    char     *srcfile;
  187.    int         srcline;
  188.    } CONTENT;
  189.  
  190.  
  191. struct help_sig_info
  192.    {
  193.    unsigned long sig;
  194.    int         version;
  195.    unsigned long base;
  196.    } ;
  197.  
  198.  
  199. int     num_topic      = 0;      /* topics */
  200. TOPIC   *topic;
  201.  
  202. int     num_label      = 0;      /* labels */
  203. LABEL   *label;
  204.  
  205. int     num_plabel      = 0;      /* private labels */
  206. LABEL   *plabel;
  207.  
  208. int     num_link      = 0;      /* all links */
  209. LINK    *a_link          = 0;
  210.  
  211. int     num_contents      = 0;      /* the table-of-contents */
  212. CONTENT *contents;
  213.  
  214. int     quiet_mode      = 0;      /* true if "/Q" option used */
  215.  
  216. int     max_pages      = 0;      /* max. pages in any topic */
  217. int     max_links      = 0;      /* max. links on any page */
  218. int     num_doc_pages      = 0;      /* total number of pages in document */
  219.  
  220. FILE    *srcfile;          /* .SRC file */
  221. int     srcline      = 0;      /* .SRC line number (used for errors) */
  222. int     srccol       = 0;      /* .SRC column. */
  223.  
  224. int     version      = -1;   /* help file version */
  225.  
  226. int     errors       = 0,      /* number of errors reported */
  227.      warnings      = 0;      /* number of warnings reported */
  228.  
  229. char     src_fname[81]      = "";   /* command-line .SRC filename */
  230. char     hdr_fname[81]      = "";   /* .H filename */
  231. char     hlp_fname[81]      = "";   /* .HLP filename */
  232. char    *src_cfname      = NULL; /* current .SRC filename */
  233.  
  234. int     format_exclude   = 0;      /* disable formatting at this col, 0 to */
  235.                   /*    never disable formatting */
  236. FILE    *swapfile;
  237. long     swappos;
  238.  
  239. char    *buffer;          /* alloc'ed as BUFFER_SIZE bytes */
  240. char    *curr;              /* current position in the buffer */
  241. char     cmd[128];          /* holds the current command */
  242. int     compress_spaces;
  243. int     xonline;
  244. int     xdoc;
  245.  
  246. #define  MAX_INCLUDE_STACK (5)      /* allow 5 nested includes */
  247.  
  248. struct
  249.    {
  250.    char *fname;
  251.    FILE *file;
  252.    int     line;
  253.    int     col;
  254.    } include_stack[MAX_INCLUDE_STACK];
  255. int include_stack_top = -1;
  256.  
  257.  
  258. #define CHK_BUFFER(off) { if ((unsigned)(curr+(off)) - (unsigned)buffer >= (BUFFER_SIZE-1024)) fatal(0,"Buffer overflowed -- Help topic too large."); }
  259.  
  260. #ifdef __WATCOMC__
  261. #define putw( x1, x2 )  fprintf( x2, "%c%c", x1&0xFF, x1>>8 );
  262. #endif
  263.  
  264. /*
  265.  * error/warning/message reporting functions.
  266.  */
  267.  
  268.  
  269. void report_errors(void)
  270.    {
  271.    printf("\n");
  272.    printf("Compiler Status:\n");
  273.    printf("%8d Error%c\n",     errors,   (errors==1)     ? ' ' : 's');
  274.    printf("%8d Warning%c\n",     warnings, (warnings==1) ? ' ' : 's');
  275.    }
  276.  
  277.  
  278. void print_msg(char *type, int lnum, char *format, va_list arg)
  279.    {
  280.    if (type != NULL)
  281.       {
  282.       printf("   %s", type);
  283.       if (lnum>0)
  284.      printf(" %s %d", src_cfname, lnum);
  285.       printf(": ");
  286.       }
  287.    vprintf(format, arg);
  288.    printf("\n");
  289.    }
  290.  
  291.  
  292. #ifndef XFRACT
  293. void fatal(int diff, char *format, ...)
  294. #else
  295. void fatal(va_alist)
  296.     va_dcl
  297. #endif
  298.    {
  299.    va_list arg;
  300.  
  301. #ifndef XFRACT
  302.    va_start(arg, format);
  303. #else
  304.    int diff;
  305.    char *format;
  306.    va_start(arg);
  307.    diff = va_arg(arg,int);
  308.    format = va_arg(arg,char *);
  309. #endif
  310.  
  311.    print_msg("Fatal", srcline-diff, format, arg);
  312.    va_end(arg);
  313.  
  314.    if ( errors || warnings )
  315.       report_errors();
  316.  
  317.    exit( errors + 1 );
  318.    }
  319.  
  320.  
  321. #ifndef XFRACT
  322. void error(int diff, char *format, ...)
  323. #else
  324. void error(va_alist)
  325.     va_dcl
  326. #endif
  327.    {
  328.    va_list arg;
  329.  
  330. #ifndef XFRACT
  331.    va_start(arg, format);
  332. #else
  333.    int diff;
  334.    char *format;
  335.    va_start(arg);
  336.    diff = va_arg(arg,int);
  337.    format = va_arg(arg,char *);
  338. #endif
  339.    print_msg("Error", srcline-diff, format, arg);
  340.    va_end(arg);
  341.  
  342.    if (++errors >= MAX_ERRORS && MAX_ERRORS > 0)
  343.       fatal(0,"Too many errors!");
  344.    }
  345.  
  346.  
  347. #ifndef XFRACT
  348. void warn(int diff, char *format, ...)
  349. #else
  350. void warn(va_alist)
  351.    va_dcl
  352. #endif
  353.    {
  354.    va_list arg;
  355. #ifndef XFRACT
  356.    va_start(arg, format);
  357. #else
  358.    int diff;
  359.    char *format;
  360.    va_start(arg);
  361.    diff = va_arg(arg, int);
  362.    format = va_arg(arg, char *);
  363. #endif
  364.    print_msg("Warning", srcline-diff, format, arg);
  365.    va_end(arg);
  366.  
  367.    if (++warnings >= MAX_WARNINGS && MAX_WARNINGS > 0)
  368.       fatal(0,"Too many warnings!");
  369.    }
  370.  
  371.  
  372. #ifndef XFRACT
  373. void notice(char *format, ...)
  374. #else
  375. void notice(va_alist)
  376.     va_dcl
  377. #endif
  378.    {
  379.    va_list arg;
  380. #ifndef XFRACT
  381.    va_start(arg, format);
  382. #else
  383.    char *format;
  384.  
  385.    va_start(arg);
  386.    format = va_arg(arg,char *);
  387. #endif
  388.    print_msg("Note", srcline, format, arg);
  389.    va_end(arg);
  390.    }
  391.  
  392.  
  393. #ifndef XFRACT
  394. void msg(char *format, ...)
  395. #else
  396. void msg(va_alist)
  397. va_dcl
  398. #endif
  399.    {
  400.    va_list arg;
  401. #ifdef XFRACT
  402.    char *format;
  403. #endif
  404.  
  405.    if (quiet_mode)
  406.       return;
  407. #ifndef XFRACT
  408.    va_start(arg, format);
  409. #else
  410.    va_start(arg);
  411.    format = va_arg(arg,char *);
  412. #endif
  413.    print_msg(NULL, 0, format, arg);
  414.    va_end(arg);
  415.    }
  416.  
  417.  
  418. #ifdef SHOW_ERROR_LINE
  419. #   define fatal  (printf("[%04d] ", __LINE__), fatal)
  420. #   define error  (printf("[%04d] ", __LINE__), error)
  421. #   define warn   (printf("[%04d] ", __LINE__), warn)
  422. #   define notice (printf("[%04d] ", __LINE__), notice)
  423. #   define msg      (printf((quiet_mode)?"":"[%04d] ", __LINE__), msg)
  424. #endif
  425.  
  426.  
  427. /*
  428.  * store-topic-text-to-disk stuff.
  429.  */
  430.  
  431.  
  432. void alloc_topic_text(TOPIC *t, unsigned size)
  433.    {
  434.    t->text_len = size;
  435.    t->text = swappos;
  436.    swappos += size;
  437.    fseek(swapfile, t->text, SEEK_SET);
  438.    fwrite(buffer, 1, t->text_len, swapfile);
  439.    }
  440.  
  441.  
  442. char *get_topic_text(TOPIC *t)
  443.    {
  444.    fseek(swapfile, t->text, SEEK_SET);
  445.    fread(buffer, 1, t->text_len, swapfile);
  446.    return (buffer);
  447.    }
  448.  
  449.  
  450. void release_topic_text(TOPIC *t, int save)
  451.    {
  452.    if ( save )
  453.       {
  454.       fseek(swapfile, t->text, SEEK_SET);
  455.       fwrite(buffer, 1, t->text_len, swapfile);
  456.       }
  457.    }
  458.  
  459.  
  460. /*
  461.  * memory-allocation functions.
  462.  */
  463.  
  464.  
  465. #define new(item)    (item *)newx(sizeof(item))
  466. #define delete(item) free(item)
  467.  
  468.  
  469. VOIDPTR newx(unsigned size)
  470.    {
  471.    VOIDPTR ptr;
  472.  
  473.    ptr = malloc(size);
  474.  
  475.    if (ptr == NULL)
  476.       fatal(0,"Out of memory!");
  477.  
  478.    return (ptr);
  479.    }
  480.  
  481.  
  482. VOIDPTR renewx(VOIDPTR ptr, unsigned size)
  483.    {
  484.    ptr = realloc(ptr, size);
  485.  
  486.    if (ptr == NULL)
  487.       fatal(0,"Out of memory!");
  488.  
  489.    return (ptr);
  490.    }
  491.  
  492.  
  493. char *dupstr(char *s, unsigned len)
  494.    {
  495.    char *ptr;
  496.  
  497.    if (len == 0)
  498.       len = strlen(s) + 1;
  499.  
  500.    ptr = newx(len);
  501.  
  502.    memcpy(ptr, s, len);
  503.  
  504.    return (ptr);
  505.    }
  506.  
  507.  
  508. #define LINK_ALLOC_SIZE (16)
  509.  
  510.  
  511. int add_link(LINK *l)
  512.    {
  513.    if (num_link == 0)
  514.       a_link = newx( sizeof(LINK)*LINK_ALLOC_SIZE );
  515.  
  516.    else if (num_link%LINK_ALLOC_SIZE == 0)
  517.       a_link = renewx(a_link, sizeof(LINK) * (num_link+LINK_ALLOC_SIZE) );
  518.  
  519.    a_link[num_link] = *l;
  520.  
  521.    return( num_link++ );
  522.    }
  523.  
  524.  
  525. #define PAGE_ALLOC_SIZE (4)
  526.  
  527.  
  528. int add_page(TOPIC *t, PAGE *p)
  529.    {
  530.    if (t->num_page == 0)
  531.       t->page = newx( sizeof(PAGE)*PAGE_ALLOC_SIZE );
  532.  
  533.    else if (t->num_page%PAGE_ALLOC_SIZE == 0)
  534.       t->page = renewx(t->page, sizeof(PAGE) * (t->num_page+PAGE_ALLOC_SIZE) );
  535.  
  536.    t->page[t->num_page] = *p;
  537.  
  538.    return ( t->num_page++ );
  539.    }
  540.  
  541.  
  542. #define TOPIC_ALLOC_SIZE (16)
  543.  
  544.  
  545. int add_topic(TOPIC *t)
  546.    {
  547.    if (num_topic == 0)
  548.       topic = newx( sizeof(TOPIC)*TOPIC_ALLOC_SIZE );
  549.  
  550.    else if (num_topic%TOPIC_ALLOC_SIZE == 0)
  551.       topic = renewx(topic, sizeof(TOPIC) * (num_topic+TOPIC_ALLOC_SIZE) );
  552.  
  553.    topic[num_topic] = *t;
  554.  
  555.    return ( num_topic++ );
  556.    }
  557.  
  558.  
  559. #define LABEL_ALLOC_SIZE (16)
  560.  
  561.  
  562. int add_label(LABEL *l)
  563.    {
  564.    if (l->name[0] == '@')    /* if it's a private label... */
  565.       {
  566.       if (num_plabel == 0)
  567.      plabel = newx( sizeof(LABEL)*LABEL_ALLOC_SIZE );
  568.  
  569.       else if (num_plabel%LABEL_ALLOC_SIZE == 0)
  570.      plabel = renewx(plabel, sizeof(LABEL) * (num_plabel+LABEL_ALLOC_SIZE) );
  571.  
  572.       plabel[num_plabel] = *l;
  573.  
  574.       return ( num_plabel++ );
  575.       }
  576.    else
  577.       {
  578.       if (num_label == 0)
  579.      label = newx( sizeof(LABEL)*LABEL_ALLOC_SIZE );
  580.  
  581.       else if (num_label%LABEL_ALLOC_SIZE == 0)
  582.      label = renewx(label, sizeof(LABEL) * (num_label+LABEL_ALLOC_SIZE) );
  583.  
  584.       label[num_label] = *l;
  585.  
  586.       return ( num_label++ );
  587.       }
  588.    }
  589.  
  590.  
  591. #define CONTENTS_ALLOC_SIZE (16)
  592.  
  593.  
  594. int add_content(CONTENT *c)
  595.    {
  596.    if (num_contents == 0)
  597.       contents = newx( sizeof(CONTENT)*CONTENTS_ALLOC_SIZE );
  598.  
  599.    else if (num_contents%CONTENTS_ALLOC_SIZE == 0)
  600.       contents = renewx(contents, sizeof(CONTENT) * (num_contents+CONTENTS_ALLOC_SIZE) );
  601.  
  602.    contents[num_contents] = *c;
  603.  
  604.    return ( num_contents++ );
  605.    }
  606.  
  607.  
  608. /*
  609.  * read_char() stuff
  610.  */
  611.  
  612.  
  613. #define READ_CHAR_BUFF_SIZE (32)
  614.  
  615.  
  616. int  read_char_buff[READ_CHAR_BUFF_SIZE];
  617. int  read_char_buff_pos = -1;
  618. int  read_char_sp       = 0;
  619.  
  620.  
  621. void unread_char(int ch)
  622.    /*
  623.     * Will not handle new-lines or tabs correctly!
  624.     */
  625.    {
  626.    if (read_char_buff_pos+1 >= READ_CHAR_BUFF_SIZE)
  627.       fatal(0,"Compiler Error -- Read char buffer overflow!");
  628.  
  629.    read_char_buff[++read_char_buff_pos] = ch;
  630.  
  631.    --srccol;
  632.    }
  633.  
  634.  
  635. void unread_string(char *s)
  636.    {
  637.    int p = strlen(s);
  638.  
  639.    while (p-- > 0)
  640.       unread_char(s[p]);
  641.    }
  642.  
  643.  
  644. int eos(void)     /* end-of-source ? */
  645.    {
  646.    return ( !((read_char_sp==0) && (read_char_buff_pos==0) && feof(srcfile)) );
  647.    }
  648.  
  649.  
  650. int _read_char(void)
  651.    {
  652.    int ch;
  653.  
  654.    if (srcline <= 0)
  655.       {
  656.       srcline = 1;
  657.       srccol = 0;
  658.       }
  659.  
  660.    if (read_char_buff_pos >= 0)
  661.       {
  662.       ++srccol;
  663.       return ( read_char_buff[read_char_buff_pos--] );
  664.       }
  665.  
  666.    if (read_char_sp > 0)
  667.       {
  668.       --read_char_sp;
  669.       return (' ');
  670.       }
  671.  
  672.    if ( feof(srcfile) )
  673.       return (-1);
  674.  
  675.    while (1)
  676.       {
  677.       ch = getc(srcfile);
  678.  
  679.       switch (ch)
  680.      {
  681.      case '\t':    /* expand a tab */
  682.         {
  683.         int diff = ( ( (srccol/8) + 1 ) * 8 ) - srccol;
  684.  
  685.         srccol += diff;
  686.         read_char_sp += diff;
  687.         break;
  688.         }
  689.  
  690.      case ' ':
  691.         ++srccol;
  692.         ++read_char_sp;
  693.         break;
  694.  
  695.      case '\n':
  696.         read_char_sp = 0;   /* delete spaces before a \n */
  697.         srccol = 0;
  698.         ++srcline;
  699.         return ('\n');
  700.  
  701.      case -1:            /* EOF */
  702.         if (read_char_sp > 0)
  703.            {
  704.            --read_char_sp;
  705.            return (' ');
  706.            }
  707.         return (-1);
  708.  
  709.      default:
  710.         if (read_char_sp > 0)
  711.            {
  712.            ungetc(ch, srcfile);
  713.            --read_char_sp;
  714.            return (' ');
  715.            }
  716.  
  717.         ++srccol;
  718.         return (ch);
  719.  
  720.      } /* switch */
  721.       }
  722.    }
  723.  
  724.  
  725. int read_char(void)
  726.    {
  727.    int ch;
  728.  
  729.    ch = _read_char();
  730.  
  731.    while (ch == ';' && srccol==1)    /* skip over comments */
  732.       {
  733.       ch = _read_char();
  734.  
  735.       while (ch!='\n' && ch!=-1 )
  736.      ch = _read_char();
  737.  
  738.       ch = _read_char();
  739.       }
  740.  
  741.    if (ch == '\\')   /* process an escape code */
  742.       {
  743.       ch = _read_char();
  744.  
  745.       if (ch >= '0' && ch <= '9')
  746.      {
  747.      char buff[4];
  748.      int  ctr;
  749.  
  750.      for (ctr=0; ; ctr++)
  751.         {
  752.         if ( ch<'0' || ch>'9' || ch==-1 || ctr>=3 )
  753.            {
  754.            unread_char(ch);
  755.            break;
  756.            }
  757.         buff[ctr] = ch;
  758.         ch = _read_char();
  759.         }
  760.      buff[ctr] = '\0';
  761.      ch = atoi(buff);
  762.      }
  763.  
  764. #ifdef XFRACT
  765.    /* Convert graphics arrows into keyboard chars */
  766.        if (ch>=24 && ch<=27) {
  767.        ch = "KJHL"[ch-24];
  768.        }
  769. #endif
  770.       ch |= 0x100;
  771.       }
  772.  
  773.    if ( (ch & 0xFF) == 0 )
  774.       {
  775.       error(0,"Null character (\'\\0\') not allowed!");
  776.       ch = 0x1FF; /* since we've had an error the file will not be written; */
  777.           /*   the value we return doesn't really matter */
  778.       }
  779.  
  780.    return(ch);
  781.    }
  782.  
  783.  
  784. /*
  785.  * misc. search functions.
  786.  */
  787.  
  788.  
  789. LABEL *find_label(char *name)
  790.    {
  791.    int      l;
  792.    LABEL *lp;
  793.  
  794.    if (*name == '@')
  795.       {
  796.       for (l=0, lp=plabel; l<num_plabel; l++, lp++)
  797.      if ( strcmp(name, lp->name) == 0 )
  798.         return (lp);
  799.       }
  800.    else
  801.       {
  802.       for (l=0, lp=label; l<num_label; l++, lp++)
  803.      if ( strcmp(name, lp->name) == 0 )
  804.         return (lp);
  805.       }
  806.  
  807.    return (NULL);
  808.    }
  809.  
  810.  
  811. int find_topic_title(char *title)
  812.    {
  813.    int t;
  814.    int len;
  815.  
  816.    while (*title == ' ')
  817.       ++title;
  818.  
  819.    len = strlen(title) - 1;
  820.    while ( title[len] == ' ' && len > 0 )
  821.       --len;
  822.  
  823.    ++len;
  824.  
  825.    if ( len > 2 && title[0] == '\"' && title[len-1] == '\"' )
  826.       {
  827.       ++title;
  828.       len -= 2;
  829.       }
  830.  
  831.    for (t=0; t<num_topic; t++)
  832.       if ( strlen(topic[t].title) == len &&
  833.        strnicmp(title, topic[t].title, len) == 0 )
  834.      return (t);
  835.  
  836.    return (-1);   /* not found */
  837.    }
  838.  
  839.  
  840. /*
  841.  * .SRC file parser stuff
  842.  */
  843.  
  844.  
  845. int validate_label_name(char *name)
  846.    {
  847.    if ( !isalpha(*name) && *name!='@' && *name!='_' )
  848.       return (0);  /* invalid */
  849.  
  850.    while (*(++name) != '\0')
  851.       if ( !isalpha(*name) && !isdigit(*name) && *name!='_' )
  852.      return(0);  /* invalid */
  853.  
  854.    return (1);  /* valid */
  855.    }
  856.  
  857.  
  858. char *read_until(char *buff, int len, char *stop_chars)
  859.    {
  860.    int ch;
  861.  
  862.    while ( --len > 0 )
  863.       {
  864.       ch = read_char();
  865.  
  866.       if ( ch == -1 )
  867.      {
  868.      *buff++ = '\0';
  869.      break;
  870.      }
  871.  
  872.       if ( (ch&0xFF) <= MAX_CMD )
  873.      *buff++ = CMD_LITERAL;
  874.  
  875.       *buff++ = ch;
  876.  
  877.       if ( (ch&0x100)==0 && strchr(stop_chars, ch) != NULL )
  878.      break;
  879.       }
  880.  
  881.    return ( buff-1 );
  882.    }
  883.  
  884.  
  885. void skip_over(char *skip)
  886.    {
  887.    int ch;
  888.  
  889.    while (1)
  890.       {
  891.       ch = read_char();
  892.  
  893.       if ( ch == -1 )
  894.      break;
  895.  
  896.       else if ( (ch&0x100) == 0 && strchr(skip, ch) == NULL )
  897.      {
  898.      unread_char(ch);
  899.      break;
  900.      }
  901.       }
  902.    }
  903.  
  904.  
  905. char *pchar(int ch)
  906.    {
  907.    static char buff[16];
  908.  
  909.    if ( ch >= 0x20 && ch <= 0x7E )
  910.       sprintf(buff, "\'%c\'", ch);
  911.    else
  912.       sprintf(buff, "\'\\x%02X\'", ch&0xFF);
  913.  
  914.    return (buff);
  915.    }
  916.  
  917.  
  918. void put_spaces(int how_many)
  919.    {
  920.    if (how_many > 2 && compress_spaces)
  921.       {
  922.       if (how_many > 255)
  923.      {
  924.      error(0,"Too many spaces (over 255).");
  925.      how_many = 255;
  926.      }
  927.  
  928.       *curr++ = CMD_SPACE;
  929.       *curr++ = (BYTE)how_many;
  930.       }
  931.    else
  932.       {
  933.       while (how_many-- > 0)
  934.      *curr++ = ' ';
  935.       }
  936.    }
  937.  
  938.  
  939. int get_next_item(void)   /* used by parse_contents() */
  940.    {
  941.    int     last;
  942.    char *ptr;
  943.  
  944.    skip_over(" \t\n");
  945.    ptr = read_until(cmd, 128, ",}");
  946.    last = (*ptr == '}');
  947.    --ptr;
  948.    while ( ptr >= cmd && strchr(" \t\n",*ptr) )   /* strip trailing spaces */
  949.       --ptr;
  950.    *(++ptr) = '\0';
  951.  
  952.    return (last);
  953.    }
  954.  
  955.  
  956. void process_contents(void)
  957.    {
  958.    CONTENT c;
  959.    char   *ptr;
  960.    int       indent;
  961.    int       ch;
  962.    TOPIC   t;
  963.  
  964.    t.flags     = 0;
  965.    t.title_len = strlen(DOCCONTENTS_TITLE)+1;
  966.    t.title     = dupstr(DOCCONTENTS_TITLE, t.title_len);
  967.    t.doc_page  = -1;
  968.    t.num_page  = 0;
  969.  
  970.    curr = buffer;
  971.  
  972.    c.flags = 0;
  973.    c.id = dupstr("",1);
  974.    c.name = dupstr("",1);
  975.    c.doc_page = -1;
  976.    c.page_num_pos = 0;
  977.    c.num_topic = 1;
  978.    c.is_label[0] = 0;
  979.    c.topic_name[0] = dupstr(DOCCONTENTS_TITLE,0);
  980.    c.srcline = -1;
  981.    add_content(&c);
  982.  
  983.    while (1)
  984.       {
  985.       ch = read_char();
  986.  
  987.       if (ch == '{')   /* process a CONTENT entry */
  988.      {
  989.      int last;
  990.  
  991.      c.flags = 0;
  992.      c.num_topic = 0;
  993.      c.doc_page = -1;
  994.      c.srcfile = src_cfname;
  995.      c.srcline = srcline;
  996.  
  997.      if ( get_next_item() )
  998.         {
  999.         error(0,"Unexpected end of DocContent entry.");
  1000.         continue;
  1001.         }
  1002.      c.id = dupstr(cmd,0);
  1003.  
  1004.      if ( get_next_item() )
  1005.         {
  1006.         error(0,"Unexpected end of DocContent entry.");
  1007.         continue;
  1008.         }
  1009.      indent = atoi(cmd);
  1010.  
  1011.      last = get_next_item();
  1012.  
  1013.      if ( cmd[0] == '\"' )
  1014.         {
  1015.         ptr = cmd+1;
  1016.         if (ptr[strlen(ptr)-1] == '\"')
  1017.            ptr[strlen(ptr)-1] = '\0';
  1018.         else
  1019.            warn(0,"Missing ending quote.");
  1020.  
  1021.         c.is_label[c.num_topic] = 0;
  1022.         c.topic_name[c.num_topic] = dupstr(ptr,0);
  1023.         ++c.num_topic;
  1024.         c.name = dupstr(ptr,0);
  1025.         }
  1026.      else
  1027.         c.name = dupstr(cmd,0);
  1028.  
  1029.      /* now, make the entry in the buffer */
  1030.  
  1031.      sprintf(curr, "%-5s %*.0s%s", c.id, indent*2, "", c.name);
  1032.      ptr = curr + strlen(curr);
  1033.      while ( (ptr-curr) < PAGE_WIDTH-10 )
  1034.         *ptr++ = '.';
  1035.      c.page_num_pos = (unsigned) ( (ptr-3) - buffer );
  1036.      curr = ptr;
  1037.  
  1038.      while (!last)
  1039.         {
  1040.         last = get_next_item();
  1041.  
  1042.         if ( stricmp(cmd, "FF") == 0 )
  1043.            {
  1044.            if ( c.flags & CF_NEW_PAGE )
  1045.           warn(0,"FF already present in this entry.");
  1046.            c.flags |= CF_NEW_PAGE;
  1047.            continue;
  1048.            }
  1049.  
  1050.         if (cmd[0] == '\"')
  1051.            {
  1052.            ptr = cmd+1;
  1053.            if (ptr[strlen(ptr)-1] == '\"')
  1054.           ptr[strlen(ptr)-1] = '\0';
  1055.            else
  1056.           warn(0,"Missing ending quote.");
  1057.  
  1058.            c.is_label[c.num_topic] = 0;
  1059.            c.topic_name[c.num_topic] = dupstr(ptr,0);
  1060.            }
  1061.         else
  1062.            {
  1063.            c.is_label[c.num_topic] = 1;
  1064.            c.topic_name[c.num_topic] = dupstr(cmd,0);
  1065.            }
  1066.  
  1067.         if ( ++c.num_topic >= MAX_CONTENT_TOPIC )
  1068.            {
  1069.            error(0,"Too many topics in DocContent entry.");
  1070.            break;
  1071.            }
  1072.         }
  1073.  
  1074.      add_content(&c);
  1075.      }
  1076.  
  1077.       else if (ch == '~')   /* end at any command */
  1078.      {
  1079.      unread_char(ch);
  1080.      break;
  1081.      }
  1082.  
  1083.       else
  1084.      *curr++ = ch;
  1085.  
  1086.       CHK_BUFFER(0);
  1087.       }
  1088.  
  1089.    alloc_topic_text(&t, (unsigned) (curr - buffer) );
  1090.    add_topic(&t);
  1091.    }
  1092.  
  1093.  
  1094. int parse_link(void)   /* returns length of link or 0 on error */
  1095.    {
  1096.    char *ptr;
  1097.    char *end;
  1098.    int     bad = 0;
  1099.    int     len;
  1100.    LINK  l;
  1101.    int     lnum;
  1102.    int     err_off;
  1103.  
  1104.    l.srcfile  = src_cfname;
  1105.    l.srcline  = srcline;
  1106.    l.doc_page = -1;
  1107.  
  1108.    end = read_until(cmd, 128, "}\n");   /* get the entire hot-link */
  1109.  
  1110.    if (*end == '\0')
  1111.       {
  1112.       error(0,"Unexpected EOF in hot-link.");
  1113.       return (0);
  1114.       }
  1115.  
  1116.    if (*end == '\n')
  1117.       {
  1118.       err_off = 1;
  1119.       warn(1,"Hot-link has no closing curly-brace (\'}\').");
  1120.       }
  1121.    else
  1122.       err_off = 0;
  1123.  
  1124.    *end = '\0';
  1125.  
  1126.    if (cmd[0] == '=')   /* it's an "explicit" link to a label or "special" */
  1127.       {
  1128.       ptr = strchr(cmd, ' ');
  1129.  
  1130.       if (ptr == NULL)
  1131.      ptr = end;
  1132.       else
  1133.      *ptr++ = '\0';
  1134.  
  1135.       len = (int) (end - ptr);
  1136.  
  1137.       if ( cmd[1] == '-' )
  1138.      {
  1139.      l.type      = 2;       /* type 2 = "special" */
  1140.      l.topic_num = atoi(cmd+1);
  1141.      l.topic_off = 0;
  1142.      l.name      = NULL;
  1143.      }
  1144.       else
  1145.      {
  1146.      l.type = 1;           /* type 1 = to a label */
  1147.      if (strlen(cmd) > 32)
  1148.         warn(err_off, "Label is long.");
  1149.      if (cmd[1] == '\0')
  1150.         {
  1151.         error(err_off, "Explicit hot-link has no Label.");
  1152.         bad = 1;
  1153.         }
  1154.      else
  1155.         l.name = dupstr(cmd+1,0);
  1156.      }
  1157.       if (len == 0)
  1158.      warn(err_off, "Explicit hot-link has no title.");
  1159.       }
  1160.    else
  1161.       {
  1162.       ptr = cmd;
  1163.       l.type = 0;   /* type 0 = topic title */
  1164.       len = (int) (end - ptr);
  1165.       if (len == 0)
  1166.      {
  1167.      error(err_off, "Implicit hot-link has no title.");
  1168.      bad = 1;
  1169.      }
  1170.       l.name = dupstr(ptr,len+1);
  1171.       l.name[len] = '\0';
  1172.       }
  1173.  
  1174.    if ( !bad )
  1175.       {
  1176.       CHK_BUFFER(1+3*sizeof(int)+len+1)
  1177.       lnum = add_link(&l);
  1178.       *curr++ = CMD_LINK;
  1179.       setint(curr,lnum);
  1180.       curr += 3*sizeof(int);
  1181.       memcpy(curr, ptr, len);
  1182.       curr += len;
  1183.       *curr++ = CMD_LINK;
  1184.       return (len);
  1185.       }
  1186.    else
  1187.       return (0);
  1188.    }
  1189.  
  1190.  
  1191. #define MAX_TABLE_SIZE (100)
  1192.  
  1193.  
  1194. int create_table(void)
  1195.    {
  1196.    char  *ptr;
  1197.    int      width;
  1198.    int      cols;
  1199.    int      start_off;
  1200.    int      first_link;
  1201.    int      rows;
  1202.    int      r, c;
  1203.    int      ch;
  1204.    int      done;
  1205.    int      len;
  1206.    int      lnum;
  1207.    int      count;
  1208.    char  *title[MAX_TABLE_SIZE];
  1209.    char  *table_start;
  1210.  
  1211.    ptr = strchr(cmd, '=');
  1212.  
  1213.    if (ptr == NULL)
  1214.       return (0);   /* should never happen! */
  1215.  
  1216.    ptr++;
  1217.  
  1218.    len = sscanf(ptr, " %d %d %d", &width, &cols, &start_off);
  1219.  
  1220.    if (len < 3)
  1221.       {
  1222.       error(1,"Too few arguments to Table.");
  1223.       return (0);
  1224.       }
  1225.  
  1226.    if (width<=0 || width > 78 || cols<=0 || start_off<0 || start_off > 78)
  1227.       {
  1228.       error(1,"Argument out of range.");
  1229.       return (0);
  1230.       }
  1231.  
  1232.    done = 0;
  1233.  
  1234.    first_link = num_link;
  1235.    table_start = curr;
  1236.    count = 0;
  1237.  
  1238.    /* first, read all the links in the table */
  1239.  
  1240.    do
  1241.       {
  1242.  
  1243.       do
  1244.      ch = read_char();
  1245.       while ( ch=='\n' || ch == ' ' );
  1246.  
  1247.       if (done)
  1248.      break;
  1249.  
  1250.       switch (ch)
  1251.      {
  1252.      case -1:
  1253.         error(0,"Unexpected EOF in a Table.");
  1254.         return(0);
  1255.  
  1256.      case '{':
  1257.         if (count >= MAX_TABLE_SIZE)
  1258.            fatal(0,"Table is too large.");
  1259.         len = parse_link();
  1260.         curr = table_start;   /* reset to the start... */
  1261.             title[count] = dupstr(curr+3*sizeof(int)+1, len+1);
  1262.         if (len >= width)
  1263.            {
  1264.            warn(1,"Link is too long; truncating.");
  1265.            len = width-1;
  1266.            }
  1267.         title[count][len] = '\0';
  1268.         ++count;
  1269.         break;
  1270.  
  1271.      case '~':
  1272.         {
  1273.         int imbedded;
  1274.  
  1275.         ch = read_char();
  1276.  
  1277.         if (ch=='(')
  1278.            imbedded = 1;
  1279.         else
  1280.            {
  1281.            imbedded = 0;
  1282.            unread_char(ch);
  1283.            }
  1284.  
  1285.         ptr = read_until(cmd, 128, ")\n,");
  1286.  
  1287.         ch = *ptr;
  1288.         *ptr = '\0';
  1289.  
  1290.         if  ( stricmp(cmd, "EndTable") == 0 )
  1291.            done = 1;
  1292.         else
  1293.            {
  1294.            error(1,"Unexpected command in table \"%s\"", cmd);
  1295.            warn(1,"Command will be ignored.");
  1296.            }
  1297.  
  1298.         if (ch == ',')
  1299.            {
  1300.            if (imbedded)
  1301.           unread_char('(');
  1302.            unread_char('~');
  1303.            }
  1304.         }
  1305.         break;
  1306.  
  1307.      default:
  1308.         error(0,"Unexpected character %s.", pchar(ch));
  1309.         break;
  1310.      }
  1311.       }
  1312.    while (!done);
  1313.  
  1314.    /* now, put all the links into the buffer... */
  1315.  
  1316.    rows = 1 + ( count / cols );
  1317.  
  1318.    for (r=0; r<rows; r++)
  1319.       {
  1320.       put_spaces(start_off);
  1321.       for (c=0; c<cols; c++)
  1322.      {
  1323.      lnum = c*rows + r;
  1324.  
  1325.      if ( first_link+lnum >= num_link )
  1326.         break;
  1327.  
  1328.      len = strlen(title[lnum]);
  1329.      *curr++ = CMD_LINK;
  1330.          setint(curr,first_link+lnum);
  1331.          curr += 3*sizeof(int);
  1332.      memcpy(curr, title[lnum], len);
  1333.      curr += len;
  1334.      *curr++ = CMD_LINK;
  1335.  
  1336.      delete(title[lnum]);
  1337.  
  1338.      if ( c < cols-1 )
  1339.         put_spaces( width-len );
  1340.      }
  1341.       *curr++ = '\n';
  1342.       }
  1343.  
  1344.    return (1);
  1345.    }
  1346.  
  1347.  
  1348. void process_comment(void)
  1349.    {
  1350.    int ch;
  1351.  
  1352.    while ( 1 )
  1353.       {
  1354.       ch = read_char();
  1355.  
  1356.       if (ch == '~')
  1357.      {
  1358.      int   imbedded;
  1359.      char *ptr;
  1360.  
  1361.      ch = read_char();
  1362.  
  1363.      if (ch=='(')
  1364.         imbedded = 1;
  1365.      else
  1366.         {
  1367.         imbedded = 0;
  1368.         unread_char(ch);
  1369.         }
  1370.  
  1371.      ptr = read_until(cmd, 128, ")\n,");
  1372.  
  1373.      ch = *ptr;
  1374.      *ptr = '\0';
  1375.  
  1376.      if  ( stricmp(cmd, "EndComment") == 0 )
  1377.         {
  1378.         if (ch == ',')
  1379.            {
  1380.            if (imbedded)
  1381.           unread_char('(');
  1382.            unread_char('~');
  1383.            }
  1384.         break;
  1385.         }
  1386.      }
  1387.  
  1388.       else if ( ch == -1 )
  1389.      {
  1390.      error(0,"Unexpected EOF in Comment");
  1391.      break;
  1392.      }
  1393.       }
  1394.    }
  1395.  
  1396.  
  1397. void process_bininc(void)
  1398.    {
  1399.    int  handle;
  1400.    long len;
  1401.  
  1402.    if ( (handle=open(cmd+7, O_RDONLY|O_BINARY)) == -1 )
  1403.       {
  1404.       error(0,"Unable to open \"%s\"", cmd+7);
  1405.       return ;
  1406.       }
  1407.  
  1408.    len = filelength(handle);
  1409.  
  1410.    if ( len >= BUFFER_SIZE )
  1411.       {
  1412.       error(0,"File \"%s\" is too large to BinInc (%dK).", cmd+7, (int)(len>>10));
  1413.       close(handle);
  1414.       return ;
  1415.       }
  1416.  
  1417.    /*
  1418.     * Since we know len is less than BUFFER_SIZE (and therefore less then
  1419.     * 64K) we can treat it as an unsigned.
  1420.     */
  1421.  
  1422.    CHK_BUFFER((unsigned)len);
  1423.  
  1424.    read(handle, curr, (unsigned)len);
  1425.  
  1426.    curr += (unsigned)len;
  1427.  
  1428.    close(handle);
  1429.    }
  1430.  
  1431.  
  1432. void start_topic(TOPIC *t, char *title, int title_len)
  1433.    {
  1434.    t->flags = 0;
  1435.    t->title_len = title_len;
  1436.    t->title = dupstr(title, title_len+1);
  1437.    t->title[title_len] = '\0';
  1438.    t->doc_page = -1;
  1439.    t->num_page = 0;
  1440.    curr = buffer;
  1441.    }
  1442.  
  1443.  
  1444. void end_topic(TOPIC *t)
  1445.    {
  1446.    alloc_topic_text(t, (unsigned) (curr - buffer) );
  1447.    add_topic(t);
  1448.    }
  1449.  
  1450.  
  1451. int end_of_sentence(char *ptr)  /* true if ptr is at the end of a sentence */
  1452.    {
  1453.    if ( *ptr == ')')
  1454.       --ptr;
  1455.  
  1456.    if ( *ptr == '\"')
  1457.       --ptr;
  1458.  
  1459.    return ( *ptr=='.' || *ptr=='?' || *ptr=='!' );
  1460.    }
  1461.  
  1462.  
  1463. void add_blank_for_split(void)     /* add space at curr for merging two lines */
  1464.    {
  1465.    if ( !is_hyphen(curr-1) )   /* no spaces if it's a hyphen */
  1466.       {
  1467.       if ( end_of_sentence(curr-1) )
  1468.      *curr++ = ' ';  /* two spaces at end of a sentence */
  1469.       *curr++ = ' ';
  1470.       }
  1471.    }
  1472.  
  1473.  
  1474. void put_a_char(int ch, TOPIC *t)
  1475.    {
  1476.    if (ch == '{' && !(t->flags & TF_DATA) )   /* is it a hot-link? */
  1477.       parse_link();
  1478.    else
  1479.       {
  1480.       if ( (ch&0xFF) <= MAX_CMD)
  1481.      *curr++ = CMD_LITERAL;
  1482.       *curr++ = ch;
  1483.       }
  1484.    }
  1485.  
  1486.  
  1487. enum STATES   /* states for FSM's */
  1488.    {
  1489.    S_Start,            /* initial state, between paragraphs       */
  1490.    S_StartFirstLine,        /* spaces at start of first line           */
  1491.    S_FirstLine,         /* text on the first line               */
  1492.    S_FirstLineSpaces,        /* spaces on the first line            */
  1493.    S_StartSecondLine,        /* spaces at start of second line           */
  1494.    S_Line,            /* text on lines after the first           */
  1495.    S_LineSpaces,        /* spaces on lines after the first           */
  1496.    S_StartLine,         /* spaces at start of lines after second       */
  1497.    S_FormatDisabled,        /* format automatically disabled for this line */
  1498.    S_FormatDisabledSpaces,  /* spaces in line which format is disabled       */
  1499.    S_Spaces
  1500.    } ;
  1501.  
  1502.  
  1503. void check_command_length(int eoff, int len)
  1504.    {
  1505.    if (strlen(cmd) != len)
  1506.       error(eoff, "Invalid text after a command \"%s\"", cmd+len);
  1507.    }
  1508.  
  1509.  
  1510. void read_src(char *fname)
  1511.    {
  1512.    int      ch;
  1513.    char  *ptr;
  1514.    TOPIC  t;
  1515.    LABEL  lbl;
  1516.    char  *margin_pos = NULL;
  1517.    int      in_topic   = 0,
  1518.       formatting = 1,
  1519.       state      = S_Start,
  1520.       num_spaces = 0,
  1521.       margin     = 0,
  1522.       in_para    = 0,
  1523.       centering  = 0,
  1524.       lformat_exclude = format_exclude,
  1525.       again;
  1526.  
  1527.    xonline = xdoc = 0;
  1528.  
  1529.    src_cfname = fname;
  1530.  
  1531.    if ( (srcfile = fopen(fname, "rt")) == NULL )
  1532.       fatal(0,"Unable to open \"%s\"", fname);
  1533.  
  1534.    msg("Compiling: %s", fname);
  1535.  
  1536.    in_topic = 0;
  1537.  
  1538.    curr = buffer;
  1539.  
  1540.    while ( 1 )
  1541.       {
  1542.  
  1543.       ch = read_char();
  1544.  
  1545.       if ( ch == -1 )   /* EOF? */
  1546.      {
  1547.      if ( include_stack_top >= 0)
  1548.         {
  1549.         fclose(srcfile);
  1550.         src_cfname = include_stack[include_stack_top].fname;
  1551.         srcfile = include_stack[include_stack_top].file;
  1552.         srcline = include_stack[include_stack_top].line;
  1553.         srccol  = include_stack[include_stack_top].col;
  1554.         --include_stack_top;
  1555.         continue;
  1556.         }
  1557.      else
  1558.         {
  1559.         if (in_topic)  /* if we're in a topic, finish it */
  1560.            end_topic(&t);
  1561.         if (num_topic == 0)
  1562.            warn(0,".SRC file has no topics.");
  1563.         break;
  1564.         }
  1565.      }
  1566.  
  1567.       if (ch == '~')   /* is is a command? */
  1568.      {
  1569.      int imbedded;
  1570.      int eoff;
  1571.      int done;
  1572.  
  1573.      ch = read_char();
  1574.      if (ch == '(')
  1575.         {
  1576.         imbedded = 1;
  1577.         eoff = 0;
  1578.         }
  1579.      else
  1580.         {
  1581.         imbedded = 0;
  1582.         eoff=0;
  1583.         unread_char(ch);
  1584.         }
  1585.  
  1586.      done = 0;
  1587.  
  1588.      while ( !done )
  1589.         {
  1590.         do
  1591.            ch = read_char();
  1592.         while (ch == ' ');
  1593.         unread_char(ch);
  1594.  
  1595.         if (imbedded)
  1596.            ptr = read_until(cmd, 128, ")\n,");
  1597.         else
  1598.            ptr = read_until(cmd, 128, "\n,");
  1599.  
  1600.         done = 1;
  1601.  
  1602.         if ( *ptr == '\0' )
  1603.            {
  1604.            error(0,"Unexpected EOF in command.");
  1605.            break;
  1606.            }
  1607.  
  1608.         if (*ptr == '\n')
  1609.            ++eoff;
  1610.  
  1611.         if ( imbedded && *ptr == '\n' )
  1612.            error(eoff,"Imbedded command has no closing parend (\')\')");
  1613.  
  1614.         done = (*ptr != ',');   /* we done if it's not a comma */
  1615.  
  1616.         if ( *ptr != '\n' && *ptr != ')' && *ptr != ',' )
  1617.            {
  1618.            error(0,"Command line too long.");
  1619.            break;
  1620.            }
  1621.  
  1622.         *ptr = '\0';
  1623.  
  1624.  
  1625.         /* commands allowed anytime... */
  1626.  
  1627.         if ( strnicmp(cmd, "Topic=", 6) == 0 )
  1628.            {
  1629.            if (in_topic)  /* if we're in a topic, finish it */
  1630.           end_topic(&t);
  1631.            else
  1632.           in_topic = 1;
  1633.  
  1634.            if (cmd[6] == '\0')
  1635.           warn(eoff,"Topic has no title.");
  1636.  
  1637.            else if (strlen(cmd+6) > 70)
  1638.           error(eoff,"Topic title is too long.");
  1639.  
  1640.            else if (strlen(cmd+6) > 60)
  1641.           warn(eoff,"Topic title is long.");
  1642.  
  1643.            if ( find_topic_title(cmd+6) != -1 )
  1644.           error(eoff,"Topic title already exists.");
  1645.  
  1646.            start_topic(&t, cmd+6, (unsigned)(ptr-(cmd+6)));
  1647.            formatting = 1;
  1648.            centering = 0;
  1649.            state = S_Start;
  1650.            in_para = 0;
  1651.            num_spaces = 0;
  1652.            xonline = xdoc = 0;
  1653.            lformat_exclude = format_exclude;
  1654.            compress_spaces = 1;
  1655.            continue;
  1656.            }
  1657.  
  1658.         else if ( strnicmp(cmd, "Data=", 5) == 0 )
  1659.            {
  1660.            if (in_topic)  /* if we're in a topic, finish it */
  1661.           end_topic(&t);
  1662.            else
  1663.           in_topic = 1;
  1664.  
  1665.            if (cmd[5] == '\0')
  1666.           warn(eoff,"Data topic has no label.");
  1667.  
  1668.            if ( !validate_label_name(cmd+5) )
  1669.           {
  1670.           error(eoff,"Label \"%s\" contains illegal characters.", cmd+5);
  1671.           continue;
  1672.           }
  1673.  
  1674.            if ( find_label(cmd+5) != NULL )
  1675.           {
  1676.           error(eoff,"Label \"%s\" already exists", cmd+5);
  1677.           continue;
  1678.           }
  1679.  
  1680.            if ( cmd[5] == '@' )
  1681.           warn(eoff, "Data topic has a local label.");
  1682.  
  1683.            start_topic(&t, "", 0);
  1684.            t.flags |= TF_DATA;
  1685.  
  1686.            if (strlen(cmd+5) > 32)
  1687.           warn(eoff,"Label name is long.");
  1688.  
  1689.            lbl.name      = dupstr(cmd+5, 0);
  1690.            lbl.topic_num = num_topic;
  1691.            lbl.topic_off = 0;
  1692.            lbl.doc_page  = -1;
  1693.            add_label(&lbl);
  1694.  
  1695.            formatting = 0;
  1696.            centering = 0;
  1697.            state = S_Start;
  1698.            in_para = 0;
  1699.            num_spaces = 0;
  1700.            xonline = xdoc = 0;
  1701.            lformat_exclude = format_exclude;
  1702.            compress_spaces = 0;
  1703.            continue;
  1704.            }
  1705.  
  1706.         else if ( strnicmp(cmd, "DocContents", 11) == 0 )
  1707.            {
  1708.            check_command_length(eoff, 11);
  1709.            if (in_topic)  /* if we're in a topic, finish it */
  1710.           end_topic(&t);
  1711.            if (!done)
  1712.           {
  1713.           if (imbedded)
  1714.              unread_char('(');
  1715.           unread_char('~');
  1716.           done = 1;
  1717.           }
  1718.            compress_spaces = 1;
  1719.            process_contents();
  1720.            in_topic = 0;
  1721.            continue;
  1722.            }
  1723.  
  1724.         else if ( stricmp(cmd, "Comment") == 0 )
  1725.            {
  1726.            process_comment();
  1727.            continue;
  1728.            }
  1729.  
  1730.         else if ( strnicmp(cmd, "FormatExclude", 13) == 0 )
  1731.            {
  1732.            if (cmd[13] == '-')
  1733.           {
  1734.           check_command_length(eoff, 14);
  1735.           if ( in_topic )
  1736.              {
  1737.              if (lformat_exclude > 0)
  1738.                 lformat_exclude = -lformat_exclude;
  1739.              else
  1740.                 warn(eoff,"\"FormatExclude-\" is already in effect.");
  1741.              }
  1742.           else
  1743.              {
  1744.              if (format_exclude > 0)
  1745.                 format_exclude = -format_exclude;
  1746.              else
  1747.                 warn(eoff,"\"FormatExclude-\" is already in effect.");
  1748.              }
  1749.           }
  1750.            else if (cmd[13] == '+')
  1751.           {
  1752.           check_command_length(eoff,14);
  1753.           if ( in_topic )
  1754.              {
  1755.              if (lformat_exclude < 0)
  1756.                 lformat_exclude = -lformat_exclude;
  1757.              else
  1758.                 warn(eoff,"\"FormatExclude+\" is already in effect.");
  1759.              }
  1760.           else
  1761.              {
  1762.              if (format_exclude < 0)
  1763.                 format_exclude = -format_exclude;
  1764.              else
  1765.                 warn(eoff,"\"FormatExclude+\" is already in effect.");
  1766.              }
  1767.           }
  1768.            else if (cmd[13] == '=')
  1769.           {
  1770.           if (cmd[14] == 'n' || cmd[14] == 'N')
  1771.              {
  1772.              check_command_length(eoff,15);
  1773.              if (in_topic)
  1774.                 lformat_exclude = 0;
  1775.              else
  1776.                format_exclude = 0;
  1777.              }
  1778.           else if (cmd[14] == '\0')
  1779.              lformat_exclude = format_exclude;
  1780.           else
  1781.              {
  1782.              int n = ( ( (in_topic) ? lformat_exclude : format_exclude) < 0 ) ? -1 : 1;
  1783.  
  1784.              lformat_exclude = atoi(cmd+14);
  1785.  
  1786.              if ( lformat_exclude <= 0 )
  1787.                 {
  1788.                 error(eoff,"Invalid argument to FormatExclude=");
  1789.                 lformat_exclude = 0;
  1790.                 }
  1791.  
  1792.              lformat_exclude *= n;
  1793.  
  1794.              if ( !in_topic )
  1795.                 format_exclude = lformat_exclude;
  1796.              }
  1797.           }
  1798.            else
  1799.           error(eoff,"Invalid format for FormatExclude");
  1800.  
  1801.            continue;
  1802.            }
  1803.  
  1804.         else if ( strnicmp(cmd, "Include ", 8) == 0 )
  1805.            {
  1806.            if (include_stack_top >= MAX_INCLUDE_STACK-1)
  1807.           error(eoff, "Too many nested Includes.");
  1808.            else
  1809.           {
  1810.           ++include_stack_top;
  1811.           include_stack[include_stack_top].fname = src_cfname;
  1812.           include_stack[include_stack_top].file = srcfile;
  1813.           include_stack[include_stack_top].line = srcline;
  1814.           include_stack[include_stack_top].col  = srccol;
  1815.           strupr(cmd+8);
  1816.           if ( (srcfile = fopen(cmd+8, "rt")) == NULL )
  1817.              {
  1818.              error(eoff, "Unable to open \"%s\"", cmd+8);
  1819.              srcfile = include_stack[include_stack_top--].file;
  1820.              }
  1821.           src_cfname = dupstr(cmd+8,0);  /* never deallocate! */
  1822.           srcline = 1;
  1823.           srccol = 0;
  1824.           }
  1825.  
  1826.            continue;
  1827.            }
  1828.  
  1829.  
  1830.         /* commands allowed only before all topics... */
  1831.  
  1832.         if ( !in_topic )
  1833.            {
  1834.            if ( strnicmp(cmd, "HdrFile=", 8) == 0 )
  1835.           {
  1836.           if (hdr_fname[0] != '\0')
  1837.              warn(eoff,"Header Filename has already been defined.");
  1838.           strcpy(hdr_fname, cmd+8);
  1839.           strupr(hdr_fname);
  1840.           }
  1841.  
  1842.            else if ( strnicmp(cmd, "HlpFile=", 8) == 0 )
  1843.           {
  1844.           if (hlp_fname[0] != '\0')
  1845.              warn(eoff,"Help Filename has already been defined.");
  1846.           strcpy(hlp_fname, cmd+8);
  1847.           strupr(hlp_fname);
  1848.           }
  1849.  
  1850.            else if ( strnicmp(cmd, "Version=", 8) == 0 )
  1851.           {
  1852.           if (version != -1)   /* an unlikely value */
  1853.              warn(eoff,"Help version has already been defined");
  1854.           version = atoi(cmd+8);
  1855.           }
  1856.  
  1857.            else
  1858.           error(eoff,"Bad or unexpected command \"%s\"", cmd);
  1859.  
  1860.            continue;
  1861.            }
  1862.  
  1863.  
  1864.         /* commands allowed only in a topic... */
  1865.  
  1866.         else
  1867.            {
  1868.            if (strnicmp(cmd, "FF", 2) == 0 )
  1869.           {
  1870.           check_command_length(eoff,2);
  1871.           if ( in_para )
  1872.              *curr++ = '\n';  /* finish off current paragraph */
  1873.           *curr++ = CMD_FF;
  1874.           state = S_Start;
  1875.           in_para = 0;
  1876.           num_spaces = 0;
  1877.           }
  1878.  
  1879.            else if (strnicmp(cmd, "DocFF", 5) == 0 )
  1880.           {
  1881.           check_command_length(eoff,5);
  1882.           if ( in_para )
  1883.              *curr++ = '\n';  /* finish off current paragraph */
  1884.           if (!xonline)
  1885.              *curr++ = CMD_XONLINE;
  1886.           *curr++ = CMD_FF;
  1887.           if (!xonline)
  1888.              *curr++ = CMD_XONLINE;
  1889.           state = S_Start;
  1890.           in_para = 0;
  1891.           num_spaces = 0;
  1892.           }
  1893.  
  1894.            else if (strnicmp(cmd, "OnlineFF", 8) == 0 )
  1895.           {
  1896.           check_command_length(eoff,8);
  1897.           if ( in_para )
  1898.              *curr++ = '\n';  /* finish off current paragraph */
  1899.           if (!xdoc)
  1900.              *curr++ = CMD_XDOC;
  1901.           *curr++ = CMD_FF;
  1902.           if (!xdoc)
  1903.              *curr++ = CMD_XDOC;
  1904.           state = S_Start;
  1905.           in_para = 0;
  1906.           num_spaces = 0;
  1907.           }
  1908.  
  1909.            else if ( strnicmp(cmd, "Label=", 6) == 0 )
  1910.           {
  1911.           if (strlen(cmd+6) <= 0)
  1912.              error(eoff,"Label has no name.");
  1913.  
  1914.           else if ( !validate_label_name(cmd+6) )
  1915.              error(eoff,"Label \"%s\" contains illegal characters.", cmd+6);
  1916.  
  1917.           else if ( find_label(cmd+6) != NULL )
  1918.              error(eoff,"Label \"%s\" already exists", cmd+6);
  1919.  
  1920.           else
  1921.              {
  1922.              if (strlen(cmd+6) > 32)
  1923.                 warn(eoff,"Label name is long.");
  1924.  
  1925.             if ( (t.flags & TF_DATA) && cmd[6] == '@' )
  1926.                warn(eoff, "Data topic has a local label.");
  1927.  
  1928.              lbl.name       = dupstr(cmd+6, 0);
  1929.              lbl.topic_num = num_topic;
  1930.              lbl.topic_off = (unsigned)(curr - buffer);
  1931.              lbl.doc_page  = -1;
  1932.              add_label(&lbl);
  1933.              }
  1934.           }
  1935.  
  1936.            else if ( strnicmp(cmd, "Table=", 6) == 0 )
  1937.           {
  1938.           if ( in_para )
  1939.              {
  1940.              *curr++ = '\n';  /* finish off current paragraph */
  1941.              in_para = 0;
  1942.              num_spaces = 0;
  1943.              state = S_Start;
  1944.              }
  1945.  
  1946.           if (!done)
  1947.              {
  1948.              if (imbedded)
  1949.                 unread_char('(');
  1950.              unread_char('~');
  1951.              done = 1;
  1952.              }
  1953.  
  1954.           create_table();
  1955.           }
  1956.  
  1957.            else if ( strnicmp(cmd, "FormatExclude", 12) == 0 )
  1958.           {
  1959.           if (cmd[13] == '-')
  1960.              {
  1961.              check_command_length(eoff,14);
  1962.              if (lformat_exclude > 0)
  1963.                 lformat_exclude = -lformat_exclude;
  1964.              else
  1965.                 warn(0,"\"FormatExclude-\" is already in effect.");
  1966.              }
  1967.           else if (cmd[13] == '+')
  1968.              {
  1969.              check_command_length(eoff,14);
  1970.              if (lformat_exclude < 0)
  1971.                 lformat_exclude = -lformat_exclude;
  1972.              else
  1973.                 warn(0,"\"FormatExclude+\" is already in effect.");
  1974.              }
  1975.           else
  1976.              error(eoff,"Unexpected or invalid argument to FormatExclude.");
  1977.           }
  1978.  
  1979.            else if ( strnicmp(cmd, "Format", 6) == 0 )
  1980.           {
  1981.           if (cmd[6] == '+')
  1982.              {
  1983.              check_command_length(eoff,7);
  1984.              if ( !formatting )
  1985.                 {
  1986.                 formatting = 1;
  1987.                 in_para = 0;
  1988.                 num_spaces = 0;
  1989.                 state = S_Start;
  1990.                 }
  1991.              else
  1992.                 warn(eoff,"\"Format+\" is already in effect.");
  1993.              }
  1994.           else if (cmd[6] == '-')
  1995.              {
  1996.              check_command_length(eoff,7);
  1997.              if ( formatting )
  1998.                 {
  1999.                 if ( in_para )
  2000.                *curr++ = '\n';  /* finish off current paragraph */
  2001.                 state = S_Start;
  2002.                 in_para = 0;
  2003.                 formatting = 0;
  2004.                 num_spaces = 0;
  2005.                 state = S_Start;
  2006.                 }
  2007.              else
  2008.                 warn(eoff,"\"Format-\" is already in effect.");
  2009.              }
  2010.           else
  2011.              error(eoff,"Invalid argument to Format.");
  2012.           }
  2013.  
  2014.            else if ( strnicmp(cmd, "Online", 6) == 0 )
  2015.           {
  2016.           if (cmd[6] == '+')
  2017.              {
  2018.              check_command_length(eoff,7);
  2019.  
  2020.              if ( xonline )
  2021.                 {
  2022.                 *curr++ = CMD_XONLINE;
  2023.                 xonline = 0;
  2024.                 }
  2025.              else
  2026.                 warn(eoff,"\"Online+\" already in effect.");
  2027.              }
  2028.           else if (cmd[6] == '-')
  2029.              {
  2030.              check_command_length(eoff,7);
  2031.              if ( !xonline )
  2032.                 {
  2033.                 *curr++ = CMD_XONLINE;
  2034.                 xonline = 1;
  2035.                 }
  2036.              else
  2037.                 warn(eoff,"\"Online-\" already in effect.");
  2038.              }
  2039.           else
  2040.              error(eoff,"Invalid argument to Online.");
  2041.           }
  2042.  
  2043.            else if ( strnicmp(cmd, "Doc", 3) == 0 )
  2044.           {
  2045.           if (cmd[3] == '+')
  2046.              {
  2047.              check_command_length(eoff,4);
  2048.              if ( xdoc )
  2049.                 {
  2050.                 *curr++ = CMD_XDOC;
  2051.                 xdoc = 0;
  2052.                 }
  2053.              else
  2054.                 warn(eoff,"\"Doc+\" already in effect.");
  2055.              }
  2056.           else if (cmd[3] == '-')
  2057.              {
  2058.              check_command_length(eoff,4);
  2059.              if ( !xdoc )
  2060.                 {
  2061.                 *curr++ = CMD_XDOC;
  2062.                 xdoc = 1;
  2063.                 }
  2064.              else
  2065.                 warn(eoff,"\"Doc-\" already in effect.");
  2066.              }
  2067.           else
  2068.              error(eoff,"Invalid argument to Doc.");
  2069.           }
  2070.  
  2071.            else if ( strnicmp(cmd, "Center", 6) == 0 )
  2072.           {
  2073.           if (cmd[6] == '+')
  2074.              {
  2075.              check_command_length(eoff,7);
  2076.              if ( !centering )
  2077.                 {
  2078.                 centering = 1;
  2079.                 if ( in_para )
  2080.                {
  2081.                *curr++ = '\n';
  2082.                in_para = 0;
  2083.                }
  2084.                 state = S_Start;  /* for centering FSM */
  2085.                 }
  2086.              else
  2087.                 warn(eoff,"\"Center+\" already in effect.");
  2088.              }
  2089.           else if (cmd[6] == '-')
  2090.              {
  2091.              check_command_length(eoff,7);
  2092.              if ( centering )
  2093.                 {
  2094.                 centering = 0;
  2095.                 state = S_Start;  /* for centering FSM */
  2096.                 }
  2097.              else
  2098.                 warn(eoff,"\"Center-\" already in effect.");
  2099.              }
  2100.           else
  2101.              error(eoff,"Invalid argument to Center.");
  2102.           }
  2103.  
  2104.            else if ( strnicmp(cmd, "CompressSpaces", 14) == 0 )
  2105.           {
  2106.           check_command_length(eoff,15);
  2107.  
  2108.           if ( cmd[14] == '+' )
  2109.              {
  2110.              if ( compress_spaces )
  2111.                 warn(eoff,"\"CompressSpaces+\" is already in effect.");
  2112.              else
  2113.                 compress_spaces = 1;
  2114.              }
  2115.           else if ( cmd[14] == '-' )
  2116.              {
  2117.              if ( !compress_spaces )
  2118.                 warn(eoff,"\"CompressSpaces-\" is already in effect.");
  2119.              else
  2120.                 compress_spaces = 0;
  2121.              }
  2122.           else
  2123.              error(eoff,"Invalid argument to CompressSpaces.");
  2124.           }
  2125.  
  2126.            else if ( strnicmp("BinInc ", cmd, 7) == 0 )
  2127.           {
  2128.           if ( !(t.flags & TF_DATA) )
  2129.              error(eoff,"BinInc allowed only in Data topics.");
  2130.           else
  2131.              process_bininc();
  2132.           }
  2133.  
  2134.            else
  2135.           error(eoff,"Bad or unexpected command \"%s\".", cmd);
  2136.            } /* else */
  2137.  
  2138.         } /* while (!done) */
  2139.  
  2140.      continue;
  2141.      }
  2142.  
  2143.       if ( !in_topic )
  2144.      {
  2145.      cmd[0] = ch;
  2146.      ptr = read_until(cmd+1, 127, "\n~");
  2147.      if (*ptr == '~')
  2148.         unread_char('~');
  2149.      *ptr = '\0';
  2150.      error(0,"Text outside of any topic \"%s\".", cmd);
  2151.      continue;
  2152.      }
  2153.  
  2154.       if ( centering )
  2155.      {
  2156.      do
  2157.         {
  2158.         again = 0;     /* default */
  2159.  
  2160.         switch (state)
  2161.            {
  2162.            case S_Start:
  2163.           if (ch == ' ')
  2164.              ; /* do nothing */
  2165.           else if ( (ch&0xFF) == '\n' )
  2166.              *curr++ = ch;  /* no need to center blank lines. */
  2167.           else
  2168.              {
  2169.              *curr++ = CMD_CENTER;
  2170.              state = S_Line;
  2171.              again = 1;
  2172.              }
  2173.           break;
  2174.  
  2175.            case S_Line:
  2176.           put_a_char(ch, &t);
  2177.           if ( (ch&0xFF) == '\n')
  2178.              state = S_Start;
  2179.           break;
  2180.            } /* switch */
  2181.         }
  2182.      while (again);
  2183.      }
  2184.  
  2185.       else if ( formatting )
  2186.      {
  2187.      int again;
  2188.  
  2189.      do
  2190.         {
  2191.         again = 0;     /* default */
  2192.  
  2193.         switch (state)
  2194.            {
  2195.            case S_Start:
  2196.           if ( (ch&0xFF) == '\n' )
  2197.              *curr++ = ch;
  2198.           else
  2199.              {
  2200.              state = S_StartFirstLine;
  2201.              num_spaces = 0;
  2202.              again = 1;
  2203.              }
  2204.           break;
  2205.  
  2206.            case S_StartFirstLine:
  2207.           if ( ch == ' ')
  2208.              ++num_spaces;
  2209.  
  2210.           else
  2211.              {
  2212.              if (lformat_exclude > 0 && num_spaces >= lformat_exclude )
  2213.                 {
  2214.                 put_spaces(num_spaces);
  2215.                 num_spaces = 0;
  2216.                 state = S_FormatDisabled;
  2217.                 again = 1;
  2218.                 }
  2219.              else
  2220.                 {
  2221.                 *curr++ = CMD_PARA;
  2222.                 *curr++ = (char)num_spaces;
  2223.                 *curr++ = (char)num_spaces;
  2224.                 margin_pos = curr - 1;
  2225.                 state = S_FirstLine;
  2226.                 again = 1;
  2227.                 in_para = 1;
  2228.                 }
  2229.              }
  2230.           break;
  2231.  
  2232.            case S_FirstLine:
  2233.           if (ch == '\n')
  2234.              {
  2235.              state = S_StartSecondLine;
  2236.              num_spaces = 0;
  2237.              }
  2238.           else if (ch == ('\n'|0x100) )   /* force end of para ? */
  2239.              {
  2240.              *curr++ = '\n';
  2241.              in_para = 0;
  2242.              state = S_Start;
  2243.              }
  2244.           else if ( ch == ' ' )
  2245.              {
  2246.              state = S_FirstLineSpaces;
  2247.              num_spaces = 1;
  2248.              }
  2249.           else
  2250.              put_a_char(ch, &t);
  2251.           break;
  2252.  
  2253.            case S_FirstLineSpaces:
  2254.           if (ch == ' ')
  2255.              ++num_spaces;
  2256.           else
  2257.              {
  2258.              put_spaces(num_spaces);
  2259.              state = S_FirstLine;
  2260.              again = 1;
  2261.              }
  2262.           break;
  2263.  
  2264.            case S_StartSecondLine:
  2265.           if ( ch == ' ')
  2266.              ++num_spaces;
  2267.           else if ((ch&0xFF) == '\n') /* a blank line means end of a para */
  2268.              {
  2269.              *curr++ = '\n';   /* end the para */
  2270.              *curr++ = '\n';   /* for the blank line */
  2271.              in_para = 0;
  2272.              state = S_Start;
  2273.              }
  2274.           else
  2275.              {
  2276.              if (lformat_exclude > 0 && num_spaces >= lformat_exclude )
  2277.                 {
  2278.                 *curr++ = '\n';
  2279.                 in_para = 0;
  2280.                 put_spaces(num_spaces);
  2281.                 num_spaces = 0;
  2282.                 state = S_FormatDisabled;
  2283.                 again = 1;
  2284.                 }
  2285.              else
  2286.                 {
  2287.                 add_blank_for_split();
  2288.                 margin = num_spaces;
  2289.                 *margin_pos = (char)num_spaces;
  2290.                 state = S_Line;
  2291.                 again = 1;
  2292.                 }
  2293.              }
  2294.           break;
  2295.  
  2296.            case S_Line:   /* all lines after the first */
  2297.           if (ch == '\n')
  2298.              {
  2299.              state = S_StartLine;
  2300.              num_spaces = 0;
  2301.              }
  2302.           else if (ch == ('\n' | 0x100) )   /* force end of para ? */
  2303.              {
  2304.              *curr++ = '\n';
  2305.              in_para = 0;
  2306.              state = S_Start;
  2307.              }
  2308.           else if ( ch == ' ' )
  2309.              {
  2310.              state = S_LineSpaces;
  2311.              num_spaces = 1;
  2312.              }
  2313.           else
  2314.              put_a_char(ch, &t);
  2315.           break;
  2316.  
  2317.            case S_LineSpaces:
  2318.           if (ch == ' ')
  2319.              ++num_spaces;
  2320.           else
  2321.              {
  2322.              put_spaces(num_spaces);
  2323.              state = S_Line;
  2324.              again = 1;
  2325.              }
  2326.           break;
  2327.  
  2328.            case S_StartLine:   /* for all lines after the second */
  2329.           if ( ch == ' ')
  2330.              ++num_spaces;
  2331.           else if ((ch&0xFF) == '\n') /* a blank line means end of a para */
  2332.              {
  2333.              *curr++ = '\n';   /* end the para */
  2334.              *curr++ = '\n';   /* for the blank line */
  2335.              in_para = 0;
  2336.              state = S_Start;
  2337.              }
  2338.           else
  2339.              {
  2340.              if ( num_spaces != margin )
  2341.                 {
  2342.                 *curr++ = '\n';
  2343.                 in_para = 0;
  2344.                 state = S_StartFirstLine;  /* with current num_spaces */
  2345.                 again = 1;
  2346.                 }
  2347.              else
  2348.                 {
  2349.                 add_blank_for_split();
  2350.                 state = S_Line;
  2351.                 again = 1;
  2352.                 }
  2353.              }
  2354.           break;
  2355.  
  2356.            case S_FormatDisabled:
  2357.           if ( ch == ' ' )
  2358.              {
  2359.              state = S_FormatDisabledSpaces;
  2360.              num_spaces = 1;
  2361.              }
  2362.           else
  2363.              {
  2364.              if ( (ch&0xFF) == '\n' )
  2365.                 state = S_Start;
  2366.              put_a_char(ch, &t);
  2367.              }
  2368.           break;
  2369.  
  2370.            case S_FormatDisabledSpaces:
  2371.           if ( ch == ' ' )
  2372.              ++num_spaces;
  2373.           else
  2374.              {
  2375.              put_spaces(num_spaces);
  2376.              num_spaces = 0;    /* is this needed? */
  2377.              state = S_FormatDisabled;
  2378.              again = 1;
  2379.              }
  2380.           break;
  2381.  
  2382.            } /* switch (state) */
  2383.         }
  2384.      while (again);
  2385.      }
  2386.  
  2387.       else
  2388.      {
  2389.      do
  2390.         {
  2391.         again = 0;     /* default */
  2392.  
  2393.         switch (state)
  2394.            {
  2395.            case S_Start:
  2396.           if ( ch == ' ' )
  2397.              {
  2398.              state = S_Spaces;
  2399.              num_spaces = 1;
  2400.              }
  2401.           else
  2402.              put_a_char(ch, &t);
  2403.           break;
  2404.  
  2405.            case S_Spaces:
  2406.           if (ch == ' ')
  2407.              ++num_spaces;
  2408.           else
  2409.              {
  2410.              put_spaces(num_spaces);
  2411.              num_spaces = 0;     /* is this needed? */
  2412.              state = S_Start;
  2413.              again = 1;
  2414.              }
  2415.           break;
  2416.            } /* switch */
  2417.         }
  2418.      while (again);
  2419.      }
  2420.  
  2421.       CHK_BUFFER(0)
  2422.       } /* while ( 1 ) */
  2423.  
  2424.    fclose(srcfile);
  2425.  
  2426.    srcline = -1;
  2427.    }
  2428.  
  2429.  
  2430. /*
  2431.  * stuff to resolve hot-link references.
  2432.  */
  2433.  
  2434.  
  2435. void make_hot_links(void)
  2436.    /*
  2437.     * calculate topic_num/topic_off for each link.
  2438.     */
  2439.    {
  2440.    LINK    *l;
  2441.    LABEL   *lbl;
  2442.    int        lctr;
  2443.    int        t;
  2444.    CONTENT *c;
  2445.    int        ctr;
  2446.  
  2447.    msg("Making hot-links.");
  2448.  
  2449.    /*
  2450.     * Calculate topic_num for all entries in DocContents.  Also set
  2451.     * "TF_IN_DOC" flag for all topics included in the document.
  2452.     */
  2453.  
  2454.    for (lctr=0, c=contents; lctr<num_contents; lctr++, c++)
  2455.       {
  2456.       for (ctr=0; ctr<c->num_topic; ctr++)
  2457.      {
  2458.      if ( c->is_label[ctr] )
  2459.         {
  2460.         lbl = find_label(c->topic_name[ctr]);
  2461.         if (lbl == NULL)
  2462.            {
  2463.            src_cfname = c->srcfile;
  2464.            srcline = c->srcline;
  2465.            error(0,"Cannot find DocContent label \"%s\".", c->topic_name[ctr]);
  2466.            srcline = -1;
  2467.            }
  2468.         else
  2469.            {
  2470.            if ( topic[lbl->topic_num].flags & TF_DATA )
  2471.           {
  2472.           src_cfname = c->srcfile;
  2473.           srcline = c->srcline;
  2474.           error(0,"Label \"%s\" is a data-only topic.", c->topic_name[ctr]);
  2475.           srcline = -1;
  2476.           }
  2477.            else
  2478.           {
  2479.           c->topic_num[ctr] = lbl->topic_num;
  2480.           if ( topic[lbl->topic_num].flags & TF_IN_DOC )
  2481.              warn(0,"Topic \"%s\" appears in document more than once.",
  2482.               topic[lbl->topic_num].title);
  2483.           else
  2484.              topic[lbl->topic_num].flags |= TF_IN_DOC;
  2485.           }
  2486.            }
  2487.  
  2488.         }
  2489.      else
  2490.         {
  2491.         t = find_topic_title(c->topic_name[ctr]);
  2492.  
  2493.         if (t == -1)
  2494.            {
  2495.            src_cfname = c->srcfile;
  2496.            srcline = c->srcline;
  2497.            error(0,"Cannot find DocContent topic \"%s\".", c->topic_name[ctr]);
  2498.            srcline = -1;  /* back to reality */
  2499.            }
  2500.         else
  2501.            {
  2502.            c->topic_num[ctr] = t;
  2503.            if ( topic[t].flags & TF_IN_DOC )
  2504.           warn(0,"Topic \"%s\" appears in document more than once.",
  2505.                topic[t].title);
  2506.            else
  2507.           topic[t].flags |= TF_IN_DOC;
  2508.            }
  2509.         }
  2510.      }
  2511.       }
  2512.  
  2513.    /*
  2514.     * Find topic_num and topic_off for all hot-links.  Also flag all hot-
  2515.     * links which will (probably) appear in the document.
  2516.     */
  2517.  
  2518.    for (lctr=0, l=a_link; lctr<num_link; l++, lctr++)
  2519.       {
  2520.       switch ( l->type )
  2521.      {
  2522.      case 0:      /* name is the title of the topic */
  2523.         t = find_topic_title(l->name);
  2524.         if (t == -1)
  2525.            {
  2526.            src_cfname = l->srcfile;
  2527.            srcline = l->srcline; /* pretend we are still in the source... */
  2528.            error(0,"Cannot find implicit hot-link \"%s\".", l->name);
  2529.            srcline = -1;  /* back to reality */
  2530.            }
  2531.         else
  2532.            {
  2533.            l->topic_num = t;
  2534.            l->topic_off = 0;
  2535.            l->doc_page = (topic[t].flags & TF_IN_DOC) ? 0 : -1;
  2536.            }
  2537.         break;
  2538.  
  2539.      case 1:  /* name is the name of a label */
  2540.         lbl = find_label(l->name);
  2541.         if (lbl == NULL)
  2542.            {
  2543.            src_cfname = l->srcfile;
  2544.            srcline = l->srcline; /* pretend again */
  2545.            error(0,"Cannot find explicit hot-link \"%s\".", l->name);
  2546.            srcline = -1;
  2547.            }
  2548.         else
  2549.            {
  2550.            if ( topic[lbl->topic_num].flags & TF_DATA )
  2551.           {
  2552.           src_cfname = l->srcfile;
  2553.           srcline = l->srcline;
  2554.           error(0,"Label \"%s\" is a data-only topic.", l->name);
  2555.           srcline = -1;
  2556.           }
  2557.            else
  2558.           {
  2559.           l->topic_num = lbl->topic_num;
  2560.           l->topic_off = lbl->topic_off;
  2561.           l->doc_page  = (topic[lbl->topic_num].flags & TF_IN_DOC) ? 0 : -1;
  2562.           }
  2563.            }
  2564.         break;
  2565.  
  2566.      case 2:   /* it's a "special" link; topic_off already has the value */
  2567.         break;
  2568.      }
  2569.       }
  2570.  
  2571.    }
  2572.  
  2573.  
  2574. /*
  2575.  * online help pagination stuff
  2576.  */
  2577.  
  2578.  
  2579. void add_page_break(TOPIC *t, int margin, char *text, char *start, char *curr, int num_links)
  2580.    {
  2581.    PAGE p;
  2582.  
  2583.    p.offset = (unsigned) (start - text);
  2584.    p.length = (unsigned) (curr - start);
  2585.    p.margin = margin;
  2586.    add_page(t, &p);
  2587.  
  2588.    if (max_links < num_links)
  2589.       max_links = num_links;
  2590.    }
  2591.  
  2592.  
  2593. void paginate_online(void)    /* paginate the text for on-line help */
  2594.    {               /* also calculates max_pages and max_links */
  2595.    int         lnum;
  2596.    char     *start;
  2597.    char     *curr;
  2598.    char     *text;
  2599.    TOPIC    *t;
  2600.    int         tctr;
  2601.    unsigned  len;
  2602.    int         skip_blanks;
  2603.    int         num_links;
  2604.    int         col;
  2605.    int         tok;
  2606.    int         size,
  2607.          width;
  2608.    int         start_margin;
  2609.  
  2610.    msg("Paginating online help.");
  2611.  
  2612.    for (t=topic, tctr=0; tctr<num_topic; t++, tctr++)
  2613.       {
  2614.       if ( t->flags & TF_DATA )
  2615.      continue;    /* don't paginate data topics */
  2616.  
  2617.       text = get_topic_text(t);
  2618.       curr = text;
  2619.       len  = t->text_len;
  2620.  
  2621.       start = curr;
  2622.       skip_blanks = 0;
  2623.       lnum = 0;
  2624.       num_links = 0;
  2625.       col = 0;
  2626.       start_margin = -1;
  2627.  
  2628.       while (len > 0)
  2629.      {
  2630.      tok = find_token_length(ONLINE, curr, len, &size, &width);
  2631.  
  2632.      switch ( tok )
  2633.         {
  2634.         case TOK_PARA:
  2635.            {
  2636.            int indent,
  2637.            margin;
  2638.  
  2639.            ++curr;
  2640.  
  2641.            indent = *curr++;
  2642.            margin = *curr++;
  2643.  
  2644.            len -= 3;
  2645.  
  2646.            col = indent;
  2647.  
  2648.            while (1)
  2649.           {
  2650.           tok = find_token_length(ONLINE, curr, len, &size, &width);
  2651.  
  2652.           if (tok == TOK_DONE || tok == TOK_NL || tok == TOK_FF )
  2653.              break;
  2654.  
  2655.           if ( tok == TOK_PARA )
  2656.              {
  2657.              col = 0;   /* fake a nl */
  2658.              ++lnum;
  2659.              break;
  2660.              }
  2661.  
  2662.           if (tok == TOK_XONLINE || tok == TOK_XDOC )
  2663.              {
  2664.              curr += size;
  2665.              len -= size;
  2666.              continue;
  2667.              }
  2668.  
  2669.           /* now tok is TOK_SPACE or TOK_LINK or TOK_WORD */
  2670.  
  2671.           if (col+width > SCREEN_WIDTH)
  2672.              {            /* go to next line... */
  2673.              if ( ++lnum >= SCREEN_DEPTH )
  2674.                 {        /* go to next page... */
  2675.                 add_page_break(t, start_margin, text, start, curr, num_links);
  2676.                 start = curr + ( (tok == TOK_SPACE) ? size : 0 );
  2677.                 start_margin = margin;
  2678.                 lnum = 0;
  2679.                 num_links = 0;
  2680.                 }
  2681.              if ( tok == TOK_SPACE )
  2682.                 width = 0;   /* skip spaces at start of a line */
  2683.  
  2684.              col = margin;
  2685.              }
  2686.  
  2687.           col += width;
  2688.           curr += size;
  2689.           len -= size;
  2690.           }
  2691.  
  2692.            skip_blanks = 0;
  2693.            width = size = 0;
  2694.            break;
  2695.            }
  2696.  
  2697.         case TOK_NL:
  2698.            if (skip_blanks && col == 0)
  2699.           {
  2700.           start += size;
  2701.           break;
  2702.           }
  2703.            ++lnum;
  2704.            if ( lnum >= SCREEN_DEPTH || (col == 0 && lnum==SCREEN_DEPTH-1) )
  2705.           {
  2706.           add_page_break(t, start_margin, text, start, curr, num_links);
  2707.           start = curr + size;
  2708.           start_margin = -1;
  2709.           lnum = 0;
  2710.           num_links = 0;
  2711.           skip_blanks = 1;
  2712.           }
  2713.            col = 0;
  2714.            break;
  2715.  
  2716.         case TOK_FF:
  2717.            col = 0;
  2718.            if (skip_blanks)
  2719.           {
  2720.           start += size;
  2721.           break;
  2722.           }
  2723.            add_page_break(t, start_margin, text, start, curr, num_links);
  2724.            start_margin = -1;
  2725.            start = curr + size;
  2726.            lnum = 0;
  2727.            num_links = 0;
  2728.            break;
  2729.  
  2730.         case TOK_DONE:
  2731.         case TOK_XONLINE:   /* skip */
  2732.         case TOK_XDOC:      /* ignore */
  2733.         case TOK_CENTER:    /* ignore */
  2734.            break;
  2735.  
  2736.         case TOK_LINK:
  2737.            ++num_links;
  2738.  
  2739.            /* fall-through */
  2740.  
  2741.         default:    /* TOK_SPACE, TOK_LINK, TOK_WORD */
  2742.            skip_blanks = 0;
  2743.            break;
  2744.  
  2745.         } /* switch */
  2746.  
  2747.      curr += size;
  2748.      len  -= size;
  2749.      col  += width;
  2750.      } /* while */
  2751.  
  2752.       if (!skip_blanks)
  2753.      add_page_break(t, start_margin, text, start, curr, num_links);
  2754.  
  2755.       if (max_pages < t->num_page)
  2756.      max_pages = t->num_page;
  2757.  
  2758.       release_topic_text(t, 0);
  2759.       } /* for */
  2760.    }
  2761.  
  2762.  
  2763. /*
  2764.  * paginate document stuff
  2765.  */
  2766.  
  2767.  
  2768. #define CNUM           0
  2769. #define TNUM           1
  2770. #define LINK_DEST_WARN 2
  2771.  
  2772.  
  2773. typedef struct
  2774.    {
  2775.    int        cnum,  /* must match above #defines so pd_get_info() will work */
  2776.         tnum,
  2777.         link_dest_warn;
  2778.  
  2779.    char far *start;
  2780.    CONTENT  *c;
  2781.    LABEL    *lbl;
  2782.  
  2783.    } PAGINATE_DOC_INFO;
  2784.  
  2785.  
  2786. LABEL *find_next_label_by_topic(int t)
  2787.    {
  2788.    LABEL *temp, *g, *p;
  2789.    int      ctr;
  2790.  
  2791.    g = p = NULL;
  2792.  
  2793.    for (temp=label, ctr=0; ctr<num_label; ctr++, temp++)
  2794.       if ( temp->topic_num == t && temp->doc_page == -1 )
  2795.      {
  2796.      g = temp;
  2797.      break;
  2798.      }
  2799.       else if (temp->topic_num > t)
  2800.      break;
  2801.  
  2802.    for (temp=plabel, ctr=0; ctr<num_plabel; ctr++, temp++)
  2803.       if ( temp->topic_num == t && temp->doc_page == -1 )
  2804.      {
  2805.      p = temp;
  2806.      break;
  2807.      }
  2808.       else if (temp->topic_num > t)
  2809.      break;
  2810.  
  2811.    if ( p == NULL )
  2812.       return (g);
  2813.  
  2814.    else if ( g == NULL )
  2815.       return (p);
  2816.  
  2817.    else
  2818.       return ( (g->topic_off < p->topic_off) ? g : p );
  2819.    }
  2820.  
  2821.  
  2822. void set_hot_link_doc_page(void)
  2823.    /*
  2824.     * Find doc_page for all hot-links.
  2825.     */
  2826.    {
  2827.    LINK  *l;
  2828.    LABEL *lbl;
  2829.    int      lctr;
  2830.    int      t;
  2831.  
  2832.    for (lctr=0, l=a_link; lctr<num_link; l++, lctr++)
  2833.       {
  2834.       switch ( l->type )
  2835.      {
  2836.      case 0:      /* name is the title of the topic */
  2837.         t = find_topic_title(l->name);
  2838.         if (t == -1)
  2839.            {
  2840.            src_cfname = l->srcfile;
  2841.            srcline = l->srcline; /* pretend we are still in the source... */
  2842.            error(0,"Cannot find implicit hot-link \"%s\".", l->name);
  2843.            srcline = -1;  /* back to reality */
  2844.            }
  2845.         else
  2846.            l->doc_page = topic[t].doc_page;
  2847.         break;
  2848.  
  2849.      case 1:  /* name is the name of a label */
  2850.         lbl = find_label(l->name);
  2851.         if (lbl == NULL)
  2852.            {
  2853.            src_cfname = l->srcfile;
  2854.            srcline = l->srcline; /* pretend again */
  2855.            error(0,"Cannot find explicit hot-link \"%s\".", l->name);
  2856.            srcline = -1;
  2857.            }
  2858.         else
  2859.            l->doc_page = lbl->doc_page;
  2860.         break;
  2861.  
  2862.      case 2:   /* special topics don't appear in the document */
  2863.         break;
  2864.      }
  2865.       }
  2866.    }
  2867.  
  2868.  
  2869. void set_content_doc_page(void)
  2870.    /*
  2871.     * insert page #'s in the DocContents
  2872.     */
  2873.    {
  2874.    CONTENT *c;
  2875.    TOPIC   *t;
  2876.    char    *base;
  2877.    int        tnum;
  2878.    int        ctr;
  2879.    char     buf[4];
  2880.    int        len;
  2881.  
  2882.    tnum = find_topic_title(DOCCONTENTS_TITLE);
  2883.    assert(tnum>=0);
  2884.    t = &topic[tnum];
  2885.  
  2886.    base = get_topic_text(t);
  2887.  
  2888.    for (ctr=1, c=contents+1; ctr<num_contents; ctr++, c++)
  2889.       {
  2890.       assert(c->doc_page>=1);
  2891.       sprintf(buf, "%d", c->doc_page);
  2892.       len = strlen(buf);
  2893.       assert(len<=3);
  2894.       memcpy(base+c->page_num_pos+(3-len), buf, len);
  2895.       }
  2896.  
  2897.    release_topic_text(t, 1);
  2898.    }
  2899.  
  2900.  
  2901. int pd_get_info(int cmd, PD_INFO *pd, int *info)
  2902.    {         /* this funtion also used by print_document() */
  2903.    CONTENT *c;
  2904.  
  2905.    switch (cmd)
  2906.       {
  2907.       case PD_GET_CONTENT:
  2908.      if ( ++info[CNUM] >= num_contents )
  2909.         return (0);
  2910.      c = &contents[info[CNUM]];
  2911.      info[TNUM] = -1;
  2912.      pd->id       = c->id;
  2913.      pd->title    = c->name;
  2914.      pd->new_page = (c->flags & CF_NEW_PAGE) ? 1 : 0;
  2915.      return (1);
  2916.  
  2917.       case PD_GET_TOPIC:
  2918.      c = &contents[info[CNUM]];
  2919.      if ( ++info[TNUM] >= c->num_topic )
  2920.         return (0);
  2921.      pd->curr = get_topic_text( &topic[c->topic_num[info[TNUM]]] );
  2922.      pd->len = topic[c->topic_num[info[TNUM]]].text_len;
  2923.      return (1);
  2924.  
  2925.       case PD_GET_LINK_PAGE:
  2926.          if ( a_link[getint(pd->s)].doc_page == -1 )
  2927.         {
  2928.         if ( info[LINK_DEST_WARN] )
  2929.            {
  2930.                src_cfname = a_link[getint(pd->s)].srcfile;
  2931.                srcline    = a_link[getint(pd->s)].srcline;
  2932.            warn(0,"Hot-link destination is not in the document.");
  2933.            srcline = -1;
  2934.            }
  2935.         return (0);
  2936.         }
  2937.          pd->i = a_link[getint(pd->s)].doc_page;
  2938.      return (1);
  2939.  
  2940.       case PD_RELEASE_TOPIC:
  2941.      c = &contents[info[CNUM]];
  2942.      release_topic_text(&topic[c->topic_num[info[TNUM]]], 0);
  2943.      return (1);
  2944.  
  2945.       default:
  2946.      return (0);
  2947.       }
  2948.    }
  2949.  
  2950.  
  2951. int paginate_doc_output(int cmd, PD_INFO *pd, PAGINATE_DOC_INFO *info)
  2952.    {
  2953.    switch (cmd)
  2954.       {
  2955.       case PD_FOOTING:
  2956.       case PD_PRINT:
  2957.       case PD_PRINTN:
  2958.       case PD_PRINT_SEC:
  2959.      return (1);
  2960.  
  2961.       case PD_HEADING:
  2962.      ++num_doc_pages;
  2963.      return (1);
  2964.  
  2965.       case PD_START_SECTION:
  2966.      info->c = &contents[info->cnum];
  2967.      return (1);
  2968.  
  2969.       case PD_START_TOPIC:
  2970.      info->start = pd->curr;
  2971.      info->lbl = find_next_label_by_topic(info->c->topic_num[info->tnum]);
  2972.      return (1);
  2973.  
  2974.       case PD_SET_SECTION_PAGE:
  2975.      info->c->doc_page = pd->pnum;
  2976.      return (1);
  2977.  
  2978.       case PD_SET_TOPIC_PAGE:
  2979.      topic[info->c->topic_num[info->tnum]].doc_page = pd->pnum;
  2980.      return (1);
  2981.  
  2982.       case PD_PERIODIC:
  2983.      while ( info->lbl != NULL && (unsigned)(pd->curr - info->start) >= info->lbl->topic_off)
  2984.         {
  2985.         info->lbl->doc_page = pd->pnum;
  2986.         info->lbl = find_next_label_by_topic(info->c->topic_num[info->tnum]);
  2987.         }
  2988.      return (1);
  2989.  
  2990.       default:
  2991.      return (0);
  2992.       }
  2993.    }
  2994.  
  2995.  
  2996. void paginate_document(void)
  2997.    {
  2998.    PAGINATE_DOC_INFO info;
  2999.  
  3000.    if (num_contents == 0)
  3001.       return ;
  3002.  
  3003.    msg("Paginating document.");
  3004.  
  3005.    info.cnum = info.tnum = -1;
  3006.    info.link_dest_warn = 1;
  3007.  
  3008.    process_document((PD_FUNC)pd_get_info, (PD_FUNC)paginate_doc_output, &info);
  3009.  
  3010.    set_hot_link_doc_page();
  3011.    set_content_doc_page();
  3012.    }
  3013.  
  3014.  
  3015. /*
  3016.  * label sorting stuff
  3017.  */
  3018.  
  3019.  
  3020. int fcmp_LABEL(VOIDCONSTPTR a, VOIDCONSTPTR b)
  3021.    {
  3022.    char *an = ((LABEL *)a)->name,
  3023.         *bn = ((LABEL *)b)->name;
  3024.    int     diff;
  3025.  
  3026.    /* compare the names, making sure that the index goes first */
  3027.  
  3028.    if ( (diff=strcmp(an,bn)) == 0 )
  3029.       return (0);
  3030.  
  3031.    if ( strcmp(an, INDEX_LABEL) == 0 )
  3032.       return (-1);
  3033.  
  3034.    if ( strcmp(bn, INDEX_LABEL) == 0 )
  3035.       return (1);
  3036.  
  3037.    return ( diff );
  3038.    }
  3039.  
  3040.  
  3041. void sort_labels(void)
  3042.    {
  3043.    qsort(label,  num_label,  sizeof(LABEL), fcmp_LABEL);
  3044.    qsort(plabel, num_plabel, sizeof(LABEL), fcmp_LABEL);
  3045.    }
  3046.  
  3047.  
  3048. /*
  3049.  * file write stuff.
  3050.  */
  3051.  
  3052.  
  3053. int compare_files(FILE *f1, FILE *f2) /* returns TRUE if different */
  3054.    {
  3055.    if ( filelength(fileno(f1)) != filelength(fileno(f2)) )
  3056.       return (1);   /* different if sizes are not the same */
  3057.  
  3058.    while ( !feof(f1) && !feof(f2) )
  3059.       if ( getc(f1) != getc(f2) )
  3060.      return (1);
  3061.  
  3062.    return ( ( feof(f1) && feof(f2) ) ? 0 : 1);
  3063.    }
  3064.  
  3065.  
  3066. void _write_hdr(char *fname, FILE *file)
  3067.    {
  3068.    int ctr;
  3069.    char nfile[MAXFILE],
  3070.         next[MAXEXT];
  3071.  
  3072.    FNSPLIT(fname, NULL, NULL, nfile, next);
  3073.    fprintf(file, "\n/*\n * %s%s\n", nfile, next);
  3074.    FNSPLIT(src_fname, NULL, NULL, nfile, next);
  3075.    fprintf(file, " *\n * Contains #defines for help.\n *\n");
  3076.    fprintf(file, " * Generated by HC from: %s%s\n *\n */\n\n\n", nfile, next);
  3077.  
  3078.    fprintf(file, "/* current help file version */\n");
  3079.    fprintf(file, "\n");
  3080.    fprintf(file, "#define %-32s %3d\n", "HELP_VERSION", version);
  3081.    fprintf(file, "\n\n");
  3082.  
  3083.    fprintf(file, "/* labels */\n\n");
  3084.  
  3085.    for (ctr=0; ctr<num_label; ctr++)
  3086.       if (label[ctr].name[0] != '@')  /* if it's not a local label... */
  3087.      {
  3088.      fprintf(file, "#define %-32s %3d", label[ctr].name, ctr);
  3089.      if ( strcmp(label[ctr].name, INDEX_LABEL) == 0 )
  3090.         fprintf(file, "        /* index */");
  3091.      fprintf(file, "\n");
  3092.      }
  3093.  
  3094.    fprintf(file, "\n\n");
  3095.    }
  3096.  
  3097.  
  3098. void write_hdr(char *fname)
  3099.    {
  3100.    FILE *temp,
  3101.         *hdr;
  3102.  
  3103.    hdr = fopen(fname, "rt");
  3104.  
  3105.    if (hdr == NULL)
  3106.       {         /* if no prev. hdr file generate a new one */
  3107.       hdr = fopen(fname, "wt");
  3108.       if (hdr == NULL)
  3109.      fatal(0,"Cannot create \"%s\".", fname);
  3110.       msg("Writing: %s", fname);
  3111.       _write_hdr(fname, hdr);
  3112.       fclose(hdr);
  3113.       notice("FRACTINT must be re-compiled.");
  3114.       return ;
  3115.       }
  3116.  
  3117.    msg("Comparing: %s", fname);
  3118.  
  3119.    temp = fopen(TEMP_FNAME, "wt");
  3120.  
  3121.    if (temp == NULL)
  3122.       fatal(0,"Cannot create temporary file: \"%s\".", TEMP_FNAME);
  3123.  
  3124.    _write_hdr(fname, temp);
  3125.  
  3126.    fclose(temp);
  3127.    temp = fopen(TEMP_FNAME, "rt");
  3128.  
  3129.    if (temp == NULL)
  3130.       fatal(0,"Cannot open temporary file: \"%s\".", TEMP_FNAME);
  3131.  
  3132.    if ( compare_files(temp, hdr) )   /* if they are different... */
  3133.       {
  3134.       msg("Updating: %s", fname);
  3135.       fclose(temp);
  3136.       fclose(hdr);
  3137.       unlink(fname);           /* delete the old hdr file */
  3138.       rename(TEMP_FNAME, fname);   /* rename the temp to the hdr file */
  3139.       notice("FRACTINT must be re-compiled.");
  3140.       }
  3141.    else
  3142.       {   /* if they are the same leave the original alone. */
  3143.       fclose(temp);
  3144.       fclose(hdr);
  3145.       unlink(TEMP_FNAME);      /* delete the temp */
  3146.       }
  3147.    }
  3148.  
  3149.  
  3150. void calc_offsets(void)    /* calc file offset to each topic */
  3151.    {
  3152.    int        t;
  3153.    TOPIC   *tp;
  3154.    long     offset;
  3155.    CONTENT *cp;
  3156.    int        c;
  3157.  
  3158.    /* NOTE: offsets do NOT include 6 bytes for signature & version! */
  3159.  
  3160.    offset = sizeof(int) +           /* max_pages */
  3161.             sizeof(int) +           /* max_links */
  3162.             sizeof(int) +           /* num_topic */
  3163.             sizeof(int) +           /* num_label */
  3164.             sizeof(int) +           /* num_contents */
  3165.             sizeof(int) +           /* num_doc_pages */
  3166.             num_topic*sizeof(long) +/* offsets to each topic */
  3167.             num_label*2*sizeof(int);/* topic_num/topic_off for all public labels */
  3168.  
  3169.    for (c=0, cp=contents; c<num_contents; c++, cp++)
  3170.       offset += sizeof(int) +       /* flags */
  3171.             1 +            /* id length */
  3172.             strlen(cp->id) +    /* id text */
  3173.             1 +            /* name length */
  3174.             strlen(cp->name) +  /* name text */
  3175.             1 +            /* number of topics */
  3176.                 cp->num_topic*sizeof(int);    /* topic numbers */
  3177.  
  3178.    for (t=0, tp=topic; t<num_topic; t++, tp++)
  3179.       {
  3180.       tp->offset = offset;
  3181.       offset += (long)sizeof(int) + /* topic flags */
  3182.                 sizeof(int) +       /* number of pages */
  3183.                 tp->num_page*3*sizeof(int) +   /* page offset, length & starting margin */
  3184.             1 +            /* length of title */
  3185.             tp->title_len +     /* title */
  3186.                 sizeof(int) +       /* length of text */
  3187.             tp->text_len;        /* text */
  3188.       }
  3189.  
  3190.    }
  3191.  
  3192.  
  3193. void insert_real_link_info(char *curr, unsigned len)
  3194.    /*
  3195.     * Replaces link indexes in the help text with topic_num, topic_off and
  3196.     * doc_page info.
  3197.     */
  3198.    {
  3199.    int         size;
  3200.    int         tok;
  3201.    LINK     *l;
  3202.  
  3203.    while (len > 0)
  3204.       {
  3205.       tok = find_token_length(0, curr, len, &size, NULL);
  3206.  
  3207.       if ( tok == TOK_LINK )
  3208.      {
  3209.          l = &a_link[ getint(curr+1) ];
  3210.          setint(curr+1,l->topic_num);
  3211.          setint(curr+1+sizeof(int),l->topic_off);
  3212.          setint(curr+1+2*sizeof(int),l->doc_page);
  3213.      }
  3214.  
  3215.       len -= size;
  3216.       curr += size;
  3217.       }
  3218.    }
  3219.  
  3220.  
  3221. void _write_help(FILE *file)
  3222.    {
  3223.    int             t, p, l, c;
  3224.    char             *text;
  3225.    TOPIC            *tp;
  3226.    CONTENT            *cp;
  3227.    struct help_sig_info  hs;
  3228.  
  3229.    /* write the signature and version */
  3230.  
  3231.    hs.sig = HELP_SIG; /* Edit line 17 of helpcom.h if this is a syntax error */
  3232.    hs.version = version;
  3233.  
  3234.    fwrite(&hs, sizeof(long)+sizeof(int), 1, file);
  3235.  
  3236.    /* write max_pages & max_links */
  3237.  
  3238.    putw(max_pages, file);
  3239.    putw(max_links, file);
  3240.  
  3241.    /* write num_topic, num_label and num_contents */
  3242.  
  3243.    putw(num_topic, file);
  3244.    putw(num_label, file);
  3245.    putw(num_contents, file);
  3246.  
  3247.    /* write num_doc_page */
  3248.  
  3249.    putw(num_doc_pages, file);
  3250.  
  3251.    /* write the offsets to each topic */
  3252.  
  3253.    for (t=0; t<num_topic; t++)
  3254.       fwrite(&topic[t].offset, sizeof(long), 1, file);
  3255.  
  3256.    /* write all public labels */
  3257.  
  3258.    for (l=0; l<num_label; l++)
  3259.       {
  3260.       putw(label[l].topic_num, file);
  3261.       putw(label[l].topic_off, file);
  3262.       }
  3263.  
  3264.    /* write contents */
  3265.  
  3266.    for (c=0, cp=contents; c<num_contents; c++, cp++)
  3267.       {
  3268.       putw(cp->flags, file);
  3269.  
  3270.       t = strlen(cp->id);
  3271.       putc((BYTE)t, file);
  3272.       fwrite(cp->id, 1, t, file);
  3273.  
  3274.       t = strlen(cp->name);
  3275.       putc((BYTE)t, file);
  3276.       fwrite(cp->name, 1, t, file);
  3277.  
  3278.       putc((BYTE)cp->num_topic, file);
  3279.       fwrite(cp->topic_num, sizeof(int), cp->num_topic, file);
  3280.       }
  3281.  
  3282.    /* write topics */
  3283.  
  3284.    for (t=0, tp=topic; t<num_topic; t++, tp++)
  3285.       {
  3286.       /* write the topics flags */
  3287.  
  3288.       putw(tp->flags, file);
  3289.  
  3290.       /* write offset, length and starting margin for each page */
  3291.  
  3292.       putw(tp->num_page, file);
  3293.       for (p=0; p<tp->num_page; p++)
  3294.      {
  3295.      putw(tp->page[p].offset, file);
  3296.      putw(tp->page[p].length, file);
  3297.      putw(tp->page[p].margin, file);
  3298.      }
  3299.  
  3300.       /* write the help title */
  3301.  
  3302.       putc((BYTE)tp->title_len, file);
  3303.       fwrite(tp->title, 1, tp->title_len, file);
  3304.  
  3305.       /* insert hot-link info & write the help text */
  3306.  
  3307.       text = get_topic_text(tp);
  3308.  
  3309.       if ( !(tp->flags & TF_DATA) )   /* don't process data topics... */
  3310.      insert_real_link_info(text, tp->text_len);
  3311.  
  3312.       putw(tp->text_len, file);
  3313.       fwrite(text, 1, tp->text_len, file);
  3314.  
  3315.       release_topic_text(tp, 0);  /* don't save the text even though        */
  3316.                   /* insert_real_link_info() modified it    */
  3317.                   /* because we don't access the info after */
  3318.                   /* this.                    */
  3319.  
  3320.       }
  3321.    }
  3322.  
  3323.  
  3324. void write_help(char *fname)
  3325.    {
  3326.    FILE *hlp;
  3327.  
  3328.    hlp = fopen(fname, "wb");
  3329.  
  3330.    if (hlp == NULL)
  3331.       fatal(0,"Cannot create .HLP file: \"%s\".", fname);
  3332.  
  3333.    msg("Writing: %s", fname);
  3334.  
  3335.    _write_help(hlp);
  3336.  
  3337.    fclose(hlp);
  3338.    }
  3339.  
  3340.  
  3341. /*
  3342.  * print document stuff.
  3343.  */
  3344.  
  3345.  
  3346. typedef struct
  3347.    {
  3348.  
  3349.    /*
  3350.     * Note: Don't move these first three or pd_get_info will work not
  3351.     *        correctly.
  3352.     */
  3353.  
  3354.    int        cnum;
  3355.    int        tnum;
  3356.    int        link_dest_warn;   /* = 0 */
  3357.  
  3358.    FILE    *file;
  3359.    int        margin;
  3360.    int        start_of_line;
  3361.    int        spaces;
  3362.    } PRINT_DOC_INFO;
  3363.  
  3364.  
  3365. void printerc(PRINT_DOC_INFO *info, int c, int n)
  3366.    {
  3367.    while ( n-- > 0 )
  3368.       {
  3369.       if (c==' ')
  3370.      ++info->spaces;
  3371.  
  3372.       else if (c=='\n' || c=='\f')
  3373.      {
  3374.      info->start_of_line = 1;
  3375.      info->spaces = 0;   /* strip spaces before a new-line */
  3376.      putc(c, info->file);
  3377.      }
  3378.  
  3379.       else
  3380.      {
  3381.      if (info->start_of_line)
  3382.         {
  3383.         info->spaces += info->margin;
  3384.         info->start_of_line = 0;
  3385.         }
  3386.  
  3387.      while (info->spaces > 0)
  3388.         {
  3389.         fputc(' ', info->file);
  3390.         --info->spaces;
  3391.         }
  3392.  
  3393.      fputc(c, info->file);
  3394.      }
  3395.       }
  3396.    }
  3397.  
  3398.  
  3399. void printers(PRINT_DOC_INFO *info, char far *s, int n)
  3400.    {
  3401.    if (n > 0)
  3402.       {
  3403.       while ( n-- > 0 )
  3404.      printerc(info, *s++, 1);
  3405.       }
  3406.    else
  3407.       {
  3408.       while ( *s != '\0' )
  3409.      printerc(info, *s++, 1);
  3410.       }
  3411.    }
  3412.  
  3413.  
  3414. int print_doc_output(int cmd, PD_INFO *pd, PRINT_DOC_INFO *info)
  3415.    {
  3416.    switch (cmd)
  3417.       {
  3418.       case PD_HEADING:
  3419.      {
  3420.      char buff[20];
  3421.  
  3422.      info->margin = 0;
  3423.      printers(info, "\n                     Fractint Version xx.xx                     Page ", 0);
  3424.      sprintf(buff, "%d\n\n", pd->pnum);
  3425.      printers(info, buff, 0);
  3426.      info->margin = PAGE_INDENT;
  3427.      return (1);
  3428.      }
  3429.  
  3430.       case PD_FOOTING:
  3431.      info->margin = 0;
  3432.      printerc(info, '\f', 1);
  3433.      info->margin = PAGE_INDENT;
  3434.      return (1);
  3435.  
  3436.       case PD_PRINT:
  3437.      printers(info, pd->s, pd->i);
  3438.      return (1);
  3439.  
  3440.       case PD_PRINTN:
  3441.      printerc(info, *pd->s, pd->i);
  3442.      return (1);
  3443.  
  3444.       case PD_PRINT_SEC:
  3445.      info->margin = TITLE_INDENT;
  3446.      if (pd->id[0] != '\0')
  3447.         {
  3448.         printers(info, pd->id, 0);
  3449.         printerc(info, ' ', 1);
  3450.         }
  3451.      printers(info, pd->title, 0);
  3452.      printerc(info, '\n', 1);
  3453.      info->margin = PAGE_INDENT;
  3454.      return (1);
  3455.  
  3456.       case PD_START_SECTION:
  3457.       case PD_START_TOPIC:
  3458.       case PD_SET_SECTION_PAGE:
  3459.       case PD_SET_TOPIC_PAGE:
  3460.       case PD_PERIODIC:
  3461.      return (1);
  3462.  
  3463.       default:
  3464.      return (0);
  3465.       }
  3466.    }
  3467.  
  3468.  
  3469. void print_document(char *fname)
  3470.    {
  3471.    PRINT_DOC_INFO info;
  3472.  
  3473.    if (num_contents == 0)
  3474.       fatal(0,".SRC has no DocContents.");
  3475.  
  3476.    msg("Printing to: %s", fname);
  3477.  
  3478.    info.cnum = info.tnum = -1;
  3479.    info.link_dest_warn = 0;
  3480.  
  3481.    if ( (info.file = fopen(fname, "wt")) == NULL )
  3482.       fatal(0,"Couldn't create \"%s\"", fname);
  3483.  
  3484.    info.margin = PAGE_INDENT;
  3485.    info.start_of_line = 1;
  3486.    info.spaces = 0;
  3487.  
  3488.    process_document((PD_FUNC)pd_get_info, (PD_FUNC)print_doc_output, &info);
  3489.  
  3490.    fclose(info.file);
  3491.    }
  3492.  
  3493.  
  3494. /*
  3495.  * compiler status and memory usage report stuff.
  3496.  */
  3497.  
  3498.  
  3499. void report_memory(void)
  3500.    {
  3501.    long string = 0,   /* bytes in strings */
  3502.         text   = 0,   /* bytes in topic text (stored on disk) */
  3503.         data   = 0,   /* bytes in active data structure */
  3504.         dead   = 0;   /* bytes in unused data structure */
  3505.    int  ctr, ctr2;
  3506.  
  3507.    for (ctr=0; ctr<num_topic; ctr++)
  3508.       {
  3509.       data   += sizeof(TOPIC);
  3510.       string += topic[ctr].title_len;
  3511.       text   += topic[ctr].text_len;
  3512.       data   += topic[ctr].num_page * sizeof(PAGE);
  3513.  
  3514.       dead   += (PAGE_ALLOC_SIZE-(topic[ctr].num_page%PAGE_ALLOC_SIZE)) * sizeof(PAGE);
  3515.       }
  3516.  
  3517.    for (ctr=0; ctr<num_link; ctr++)
  3518.       {
  3519.       data += sizeof(LINK);
  3520.       string += strlen(a_link[ctr].name);
  3521.       }
  3522.  
  3523.    if (num_link > 0)
  3524.       dead += (LINK_ALLOC_SIZE-(num_link%LINK_ALLOC_SIZE)) * sizeof(LINK);
  3525.  
  3526.    for (ctr=0; ctr<num_label; ctr++)
  3527.       {
  3528.       data   += sizeof(LABEL);
  3529.       string += strlen(label[ctr].name) + 1;
  3530.       }
  3531.  
  3532.    if (num_label > 0)
  3533.       dead += (LABEL_ALLOC_SIZE-(num_label%LABEL_ALLOC_SIZE)) * sizeof(LABEL);
  3534.  
  3535.    for (ctr=0; ctr<num_plabel; ctr++)
  3536.       {
  3537.       data   += sizeof(LABEL);
  3538.       string += strlen(plabel[ctr].name) + 1;
  3539.       }
  3540.  
  3541.    if (num_plabel > 0)
  3542.       dead += (LABEL_ALLOC_SIZE-(num_plabel%LABEL_ALLOC_SIZE)) * sizeof(LABEL);
  3543.  
  3544.    for (ctr=0; ctr<num_contents; ctr++)
  3545.       {
  3546.       int t;
  3547.  
  3548.       t = ( MAX_CONTENT_TOPIC - contents[ctr].num_topic ) *
  3549.       ( sizeof(contents[0].is_label[0])   +
  3550.         sizeof(contents[0].topic_name[0]) +
  3551.         sizeof(contents[0].topic_num[0])     );
  3552.       data += sizeof(CONTENT) - t;
  3553.       dead += t;
  3554.       string += strlen(contents[ctr].id) + 1;
  3555.       string += strlen(contents[ctr].name) + 1;
  3556.       for (ctr2=0; ctr2<contents[ctr].num_topic; ctr2++)
  3557.      string += strlen(contents[ctr].topic_name[ctr2]) + 1;
  3558.       }
  3559.  
  3560.    dead += (CONTENTS_ALLOC_SIZE-(num_contents%CONTENTS_ALLOC_SIZE)) * sizeof(CONTENT);
  3561.  
  3562.    printf("\n");
  3563.    printf("Memory Usage:\n");
  3564.    printf("%8ld Bytes in buffers.\n", (long)BUFFER_SIZE);
  3565.    printf("%8ld Bytes in strings.\n", string);
  3566.    printf("%8ld Bytes in data.\n", data);
  3567.    printf("%8ld Bytes in dead space.\n", dead);
  3568.    printf("--------\n");
  3569.    printf("%8ld Bytes total.\n", (long)BUFFER_SIZE+string+data+dead);
  3570.    printf("\n");
  3571.    printf("Disk Usage:\n");
  3572.    printf("%8ld Bytes in topic text.\n", text);
  3573.    }
  3574.  
  3575.  
  3576. void report_stats(void)
  3577.    {
  3578.    int  pages = 0;
  3579.    int        t;
  3580.  
  3581.    for (t=0; t<num_topic; t++)
  3582.       pages += topic[t].num_page;
  3583.  
  3584.    printf("\n");
  3585.    printf("Statistics:\n");
  3586.    printf("%8d Topics\n", num_topic);
  3587.    printf("%8d Links\n", num_link);
  3588.    printf("%8d Labels\n", num_label);
  3589.    printf("%8d Private labels\n", num_plabel);
  3590.    printf("%8d Table of contents (DocContent) entries\n", num_contents);
  3591.    printf("%8d Online help pages\n", pages);
  3592.    printf("%8d Document pages\n", num_doc_pages);
  3593.    }
  3594.  
  3595.  
  3596. /*
  3597.  * add/delete help from .EXE functions.
  3598.  */
  3599.  
  3600.  
  3601. void add_hlp_to_exe(char *hlp_fname, char *exe_fname)
  3602.    {
  3603.    int                exe,   /* handles */
  3604.                 hlp;
  3605.    long             len,
  3606.                 count;
  3607.    int                size;
  3608.    struct help_sig_info hs;
  3609.  
  3610.    if ( (exe=open(exe_fname, O_RDWR|O_BINARY)) == -1 )
  3611.       fatal(0,"Unable to open \"%s\"", exe_fname);
  3612.  
  3613.    if ( (hlp=open(hlp_fname, O_RDONLY|O_BINARY)) == -1 )
  3614.       fatal(0,"Unable to open \"%s\"", hlp_fname);
  3615.  
  3616.    msg("Appending %s to %s", hlp_fname, exe_fname);
  3617.  
  3618.    /* first, check and see if any help is currently installed */
  3619.  
  3620.    lseek(exe, filelength(exe) - sizeof(struct help_sig_info), SEEK_SET);
  3621.  
  3622.    read(exe, (char *)&hs, 10);
  3623.  
  3624.    if ( hs.sig == HELP_SIG )
  3625.       warn(0,"Overwriting previous help. (Version=%d)", hs.version);
  3626.    else
  3627.       hs.base = filelength(exe);
  3628.  
  3629.    /* now, let's see if their help file is for real (and get the version) */
  3630.  
  3631.    read(hlp, (char *)&hs, sizeof(long)+sizeof(int));
  3632.  
  3633.    if (hs.sig != HELP_SIG )
  3634.       fatal(0,"Help signature not found in %s", hlp_fname);
  3635.  
  3636.    msg("Help file %s Version=%d", hlp_fname, hs.version);
  3637.  
  3638.    /* append the help stuff, overwriting old help (if any) */
  3639.  
  3640.    lseek(exe, hs.base, SEEK_SET);
  3641.  
  3642.    len = filelength(hlp) - sizeof(long) - sizeof(int); /* adjust for the file signature & version */
  3643.  
  3644.    for (count=0; count<len; )
  3645.       {
  3646.       size = (int) min((long)BUFFER_SIZE, len-count);
  3647.       read(hlp, buffer, size);
  3648.       write(exe, buffer, size);
  3649.       count += size;
  3650.       }
  3651.  
  3652.    /* add on the signature, version and offset */
  3653.  
  3654.    write(exe, (char *)&hs, 10);
  3655.  
  3656.    chsize(exe, tell(exe));   /* truncate in case old help was longer */
  3657.  
  3658.    close(exe);
  3659.    close(hlp);
  3660.    }
  3661.  
  3662.  
  3663. void delete_hlp_from_exe(char *exe_fname)
  3664.    {
  3665.    int     exe;   /* file handle */
  3666.    struct help_sig_info hs;
  3667.  
  3668.    if ( (exe=open(exe_fname, O_RDWR|O_BINARY)) == -1 )
  3669.       fatal(0,"Unable to open \"%s\"", exe_fname);
  3670.  
  3671.    msg("Deleting help from %s", exe_fname);
  3672.  
  3673.    /* see if any help is currently installed */
  3674.  
  3675. #ifndef XFRACT
  3676.    lseek(exe, filelength(exe) - 10, SEEK_SET);
  3677.    read(exe, (char *)&hs, 10);
  3678. #else
  3679.    lseek(exe, filelength(exe) - 12, SEEK_SET);
  3680.    read(exe, (char *)&hs, 12);
  3681. #endif
  3682.  
  3683.    if ( hs.sig == HELP_SIG )
  3684.       {
  3685.       chsize(exe, hs.base);   /* truncate at the start of the help */
  3686.       close(exe);
  3687.       }
  3688.    else
  3689.       {
  3690.       close(exe);
  3691.       fatal(0,"No help found in %s", exe_fname);
  3692.       }
  3693.    }
  3694.  
  3695.  
  3696. /*
  3697.  * command-line parser, etc.
  3698.  */
  3699.  
  3700.  
  3701. #define MODE_COMPILE 1
  3702. #define MODE_PRINT   2
  3703. #define MODE_APPEND  3
  3704. #define MODE_DELETE  4
  3705.  
  3706.  
  3707. int main(int argc, char *argv[])
  3708.    {
  3709.    int      show_stats = 0,
  3710.       show_mem   = 0;
  3711.    int      mode         = 0;
  3712.  
  3713.    char **arg;
  3714.    char   fname1[81],
  3715.       fname2[81];
  3716.    char   swappath[81];
  3717.  
  3718.    fname1[0] = fname2[0] = swappath[0] = 0;
  3719.  
  3720.    printf("HC - FRACTINT Help Compiler.\n\n");
  3721.  
  3722.    buffer = malloc(BUFFER_SIZE);
  3723.  
  3724.    if (buffer == NULL)
  3725.       fatal(0,"Not enough memory to allocate buffer.");
  3726.  
  3727.    for (arg= &argv[1]; argc>1; argc--, arg++)
  3728.       {
  3729.       switch ( (*arg)[0] )
  3730.      {
  3731.      case '/':
  3732.      case '-':
  3733.         switch ( (*arg)[1] )
  3734.            {
  3735.            case 'c':
  3736.           if (mode == 0)
  3737.              mode = MODE_COMPILE;
  3738.           else
  3739.              fatal(0,"Cannot have /c with /a, /d or /p");
  3740.           break;
  3741.  
  3742.            case 'a':
  3743.           if (mode == 0)
  3744.              mode = MODE_APPEND;
  3745.           else
  3746.              fatal(0,"Cannot have /a with /c, /d or /p");
  3747.           break;
  3748.  
  3749.            case 'd':
  3750.           if (mode == 0)
  3751.              mode = MODE_DELETE;
  3752.           else
  3753.              fatal(0,"Cannot have /d with /c, /a or /p");
  3754.           break;
  3755.  
  3756.            case 'p':
  3757.           if (mode == 0)
  3758.              mode = MODE_PRINT;
  3759.           else
  3760.              fatal(0,"Cannot have /p with /c, /a or /d");
  3761.           break;
  3762.  
  3763.            case 'm':
  3764.           if (mode == MODE_COMPILE)
  3765.              show_mem = 1;
  3766.           else
  3767.              fatal(0,"/m switch allowed only when compiling (/c)");
  3768.           break;
  3769.  
  3770.            case 's':
  3771.           if (mode == MODE_COMPILE)
  3772.              show_stats = 1;
  3773.           else
  3774.              fatal(0,"/s switch allowed only when compiling (/c)");
  3775.           break;
  3776.  
  3777.            case 'r':
  3778.           if (mode == MODE_COMPILE || mode == MODE_PRINT)
  3779.              strcpy(swappath, (*arg)+2);
  3780.           else
  3781.              fatal(0,"/r switch allowed when compiling (/c) or printing (/p)");
  3782.           break;
  3783.  
  3784.            case 'q':
  3785.           quiet_mode = 1;
  3786.           break;
  3787.  
  3788.            default:
  3789.           fatal(0,"Bad command-line switch /%c", (*arg)[1]);
  3790.           break;
  3791.            }
  3792.         break;
  3793.  
  3794.      default:   /* assume it is a fname */
  3795.         if (fname1[0] == '\0')
  3796.            strcpy(fname1, *arg);
  3797.         else if (fname2[0] == '\0')
  3798.            strcpy(fname2, *arg);
  3799.         else
  3800.            fatal(0,"Unexpected command-line argument \"%s\"", *arg);
  3801.         break;
  3802.      } /* switch */
  3803.       } /* for */
  3804.  
  3805.    strupr(fname1);
  3806.    strupr(fname2);
  3807.    strupr(swappath);
  3808.  
  3809.    switch (mode)
  3810.       {
  3811.       case 0:
  3812.          printf( "To compile a .SRC file:\n");
  3813.          printf( "      HC /c [/s] [/m] [/r[path]] [src_file]\n");
  3814.          printf( "         /s       = report statistics.\n");
  3815.          printf( "         /m       = report memory usage.\n");
  3816.          printf( "         /r[path] = set swap file path.\n");
  3817.          printf( "         src_file = .SRC file.  Default is \"%s\"\n", DEFAULT_SRC_FNAME);
  3818.          printf( "To print a .SRC file:\n");
  3819.          printf( "      HC /p [/r[path]] [src_file] [out_file]\n");
  3820.          printf( "         /r[path] = set swap file path.\n");
  3821.          printf( "         src_file = .SRC file.  Default is \"%s\"\n", DEFAULT_SRC_FNAME);
  3822.          printf( "         out_file = Filename to print to. Default is \"%s\"\n",
  3823.          DEFAULT_DOC_FNAME);
  3824.          printf( "To append a .HLP file to an .EXE file:\n");
  3825.          printf( "      HC /a [hlp_file] [exe_file]\n");
  3826.          printf( "         hlp_file = .HLP file.  Default is \"%s\"\n", DEFAULT_HLP_FNAME);
  3827.          printf( "         exe_file = .EXE file.  Default is \"%s\"\n", DEFAULT_EXE_FNAME);
  3828.          printf( "To delete help info from an .EXE file:\n");
  3829.          printf( "      HC /d [exe_file]\n");
  3830.          printf( "         exe_file = .EXE file.  Default is \"%s\"\n", DEFAULT_EXE_FNAME);
  3831.          printf( "\n");
  3832.          printf( "Use \"/q\" for quiet mode. (No status messages.)\n");
  3833.      break;
  3834.  
  3835.       case MODE_COMPILE:
  3836.      if (fname2[0] != '\0')
  3837.         fatal(0,"Unexpected command-line argument \"%s\"", fname2);
  3838.  
  3839.      strcpy(src_fname, (fname1[0]=='\0') ? DEFAULT_SRC_FNAME : fname1);
  3840.  
  3841.      strcat(swappath, SWAP_FNAME);
  3842.  
  3843.      if ( (swapfile=fopen(swappath, "w+b")) == NULL )
  3844.         fatal(0,"Cannot create swap file \"%s\"", swappath);
  3845.      swappos = 0;
  3846.  
  3847.      read_src(src_fname);
  3848.  
  3849.      if (hdr_fname[0] == '\0')
  3850.         error(0,"No .H file defined.  (Use \"~HdrFile=\")");
  3851.      if (hlp_fname[0] == '\0')
  3852.         error(0,"No .HLP file defined.  (Use \"~HlpFile=\")");
  3853.      if (version == -1)
  3854.         warn(0,"No help version has been defined.  (Use \"~Version=\")");
  3855.  
  3856.      /* order of these is very important... */
  3857.  
  3858.      make_hot_links();  /* do even if errors since it may report */
  3859.                 /* more... */
  3860.  
  3861.      if ( !errors )     paginate_online();
  3862.      if ( !errors )     paginate_document();
  3863.      if ( !errors )     calc_offsets();
  3864.      if ( !errors )     sort_labels();
  3865.      if ( !errors )     write_hdr(hdr_fname);
  3866.      if ( !errors )     write_help(hlp_fname);
  3867.  
  3868.      if ( show_stats )
  3869.         report_stats();
  3870.  
  3871.      if ( show_mem )
  3872.         report_memory();
  3873.  
  3874.      if ( errors || warnings )
  3875.         report_errors();
  3876.  
  3877.      fclose(swapfile);
  3878.      remove(swappath);
  3879.  
  3880.      break;
  3881.  
  3882.       case MODE_PRINT:
  3883.      strcpy(src_fname, (fname1[0]=='\0') ? DEFAULT_SRC_FNAME : fname1);
  3884.  
  3885.      strcat(swappath, SWAP_FNAME);
  3886.  
  3887.      if ( (swapfile=fopen(swappath, "w+b")) == NULL )
  3888.         fatal(0,"Cannot create swap file \"%s\"", swappath);
  3889.      swappos = 0;
  3890.  
  3891.      read_src(src_fname);
  3892.  
  3893.      make_hot_links();
  3894.  
  3895.      if ( !errors )     paginate_document();
  3896.      if ( !errors )     print_document( (fname2[0]=='\0') ? DEFAULT_DOC_FNAME : fname2 );
  3897.  
  3898.      if ( errors || warnings )
  3899.         report_errors();
  3900.  
  3901.      fclose(swapfile);
  3902.      remove(swappath);
  3903.  
  3904.      break;
  3905.  
  3906.       case MODE_APPEND:
  3907.      add_hlp_to_exe( (fname1[0]=='\0') ? DEFAULT_HLP_FNAME : fname1,
  3908.              (fname2[0]=='\0') ? DEFAULT_EXE_FNAME : fname2);
  3909.      break;
  3910.  
  3911.       case MODE_DELETE:
  3912.      if (fname2[0] != '\0')
  3913.         fatal(0,"Unexpected argument \"%s\"", fname2);
  3914.      delete_hlp_from_exe((fname1[0]=='\0') ? DEFAULT_EXE_FNAME : fname1);
  3915.      break;
  3916.       }
  3917.  
  3918.    free(buffer);
  3919.  
  3920.    return ( errors );   /* return the number of errors */
  3921.    }
  3922.  
  3923.  
  3924.